e16fb16987
SUPER_ADMIN 토큰(company_code=*)이면 등록 회사들 DB 를 순회해 결과를 집계해 돌려주는 CrossTenantAggregator/Controller 추가. 사용자/권한그룹/ 배치/다국어 키 4개 도메인의 list API 가 cross-tenant 모드 지원. UserTable + ResponsiveDataView 에 compact/scrollContainer prop 추가. 페이지 헤더/툴바/페이지네이션은 고정, 테이블만 자체 스크롤. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
222 lines
7.4 KiB
XML
222 lines
7.4 KiB
XML
<?xml version="1.0" encoding="UTF-8" ?>
|
|
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
|
|
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
|
|
|
|
<mapper namespace="provisioning">
|
|
|
|
<select id="existsCompanyCode" parameterType="map" resultType="int">
|
|
SELECT 1 FROM COMPANY_MNG WHERE COMPANY_CODE = #{company_code} LIMIT 1
|
|
</select>
|
|
|
|
<select id="existsSubdomain" parameterType="map" resultType="int">
|
|
SELECT 1 FROM COMPANY_MNG WHERE SUBDOMAIN = #{subdomain} LIMIT 1
|
|
</select>
|
|
|
|
<select id="existsDbName" parameterType="map" resultType="int">
|
|
SELECT 1 FROM COMPANY_MNG WHERE DB_NAME = #{db_name} LIMIT 1
|
|
</select>
|
|
|
|
<insert id="insertCompanyWithTenant" parameterType="map">
|
|
INSERT INTO COMPANY_MNG (
|
|
COMPANY_CODE
|
|
, COMPANY_NAME
|
|
, BUSINESS_REGISTRATION_NUMBER
|
|
, REPRESENTATIVE_NAME
|
|
, REPRESENTATIVE_PHONE
|
|
, EMAIL
|
|
, WEBSITE
|
|
, ADDRESS
|
|
, STATUS
|
|
, DB_NAME
|
|
, SUBDOMAIN
|
|
, DB_HOST
|
|
, DB_STATUS
|
|
, PLAN
|
|
, INDUSTRY
|
|
, TEMPLATES_COUNT
|
|
, WRITER
|
|
, CREATED_DATE
|
|
) VALUES (
|
|
#{company_code}
|
|
, #{company_name}
|
|
, #{business_registration_number}
|
|
, #{representative_name}
|
|
, #{representative_phone}
|
|
, #{email}
|
|
, #{website}
|
|
, #{address}
|
|
, COALESCE(#{status}, 'active')
|
|
, #{db_name}
|
|
, #{subdomain}
|
|
, #{db_host}
|
|
, COALESCE(#{db_status}, 'provisioning')
|
|
, COALESCE(#{plan}, 'Starter')
|
|
, #{industry}
|
|
, COALESCE(#{templates_count}, 0)
|
|
, #{writer}
|
|
, NOW()
|
|
)
|
|
</insert>
|
|
|
|
<!--
|
|
회사관리 UI (v9 accordion) 렌더용 전체 목록.
|
|
정적 필드만 반환. users / active30 / db_size / spark 등 derived 는 CompanyStatsService 가 덧붙임.
|
|
-->
|
|
<select id="listCompaniesForUi" resultType="map">
|
|
SELECT
|
|
COMPANY_CODE as company_code
|
|
, COMPANY_NAME as company_name
|
|
, SUBDOMAIN as subdomain
|
|
, DB_NAME as db_name
|
|
, DB_HOST as db_host
|
|
, DB_STATUS as db_status
|
|
, STATUS as status
|
|
, COALESCE(PLAN, 'Starter') as plan
|
|
, INDUSTRY as industry
|
|
, REPRESENTATIVE_NAME as owner
|
|
, BUSINESS_REGISTRATION_NUMBER as brn
|
|
, EMAIL as email
|
|
, COALESCE(TEMPLATES_COUNT, 0) as templates
|
|
, COALESCE(DB_QUOTA_GB, 20) as db_quota_gb
|
|
, CREATED_DATE as created
|
|
, WRITER as writer
|
|
FROM COMPANY_MNG
|
|
ORDER BY CREATED_DATE DESC NULLS LAST, COMPANY_CODE
|
|
</select>
|
|
|
|
<update id="updateDbStatus" parameterType="map">
|
|
UPDATE COMPANY_MNG
|
|
SET DB_STATUS = #{db_status}
|
|
WHERE COMPANY_CODE = #{company_code}
|
|
</update>
|
|
|
|
<!-- 부팅 시 스키마 마이그레이션 러너용: 활성 테넌트 DB 이름 목록 -->
|
|
<select id="listActiveDbNames" resultType="string">
|
|
SELECT DB_NAME
|
|
FROM COMPANY_MNG
|
|
WHERE DB_STATUS = 'active'
|
|
AND DB_NAME IS NOT NULL
|
|
</select>
|
|
|
|
<!-- 회사 상세 조회 (lifecycle 관리 액션 수행 전 검증용) -->
|
|
<select id="selectCompanyByCode" parameterType="map" resultType="map">
|
|
SELECT
|
|
COMPANY_CODE as company_code
|
|
, COMPANY_NAME as company_name
|
|
, SUBDOMAIN as subdomain
|
|
, DB_NAME as db_name
|
|
, DB_HOST as db_host
|
|
, DB_STATUS as db_status
|
|
, STATUS as status
|
|
, COALESCE(INSTALLED_GROUPS, '[]'::jsonb) as installed_groups
|
|
, DEACTIVATED_AT as deactivated_at
|
|
, DEACTIVATION_REASON as deactivation_reason
|
|
, CREATED_DATE as created
|
|
FROM COMPANY_MNG
|
|
WHERE COMPANY_CODE = #{company_code}
|
|
</select>
|
|
|
|
<!-- 비활성화: DB_STATUS='suspended' + 타임스탬프/사유 기록 -->
|
|
<update id="deactivateCompany" parameterType="map">
|
|
UPDATE COMPANY_MNG
|
|
SET DB_STATUS = 'suspended'
|
|
, DEACTIVATED_AT = NOW()
|
|
, DEACTIVATION_REASON = #{reason}
|
|
WHERE COMPANY_CODE = #{company_code}
|
|
</update>
|
|
|
|
<!-- 재활성화: DB_STATUS='active' + 타임스탬프/사유 클리어 -->
|
|
<update id="reactivateCompany" parameterType="map">
|
|
UPDATE COMPANY_MNG
|
|
SET DB_STATUS = 'active'
|
|
, DEACTIVATED_AT = NULL
|
|
, DEACTIVATION_REASON = NULL
|
|
WHERE COMPANY_CODE = #{company_code}
|
|
</update>
|
|
|
|
<!-- 영구 삭제 (메타 DB row). 실제 DROP DATABASE 는 서비스 레이어가 먼저 실행 -->
|
|
<delete id="deleteCompany" parameterType="map">
|
|
DELETE FROM COMPANY_MNG
|
|
WHERE COMPANY_CODE = #{company_code}
|
|
</delete>
|
|
|
|
<!-- installed_groups 저장 (provisioning 시 selected_groups 를 JSONB 로) -->
|
|
<update id="updateInstalledGroups" parameterType="map">
|
|
UPDATE COMPANY_MNG
|
|
SET INSTALLED_GROUPS = #{installed_groups_json}::jsonb
|
|
WHERE COMPANY_CODE = #{company_code}
|
|
</update>
|
|
|
|
<!-- ─────────────── 회사 관리 감사 로그 (COMPANY_AUDIT_LOG) ─────────────── -->
|
|
|
|
<insert id="insertAuditLog" parameterType="map">
|
|
INSERT INTO COMPANY_AUDIT_LOG (
|
|
COMPANY_CODE
|
|
, ACTOR_USER_ID
|
|
, ACTION
|
|
, TARGET
|
|
, DETAILS
|
|
, SUCCESS
|
|
, ERROR_MESSAGE
|
|
, CREATED_AT
|
|
) VALUES (
|
|
#{company_code}
|
|
, #{actor_user_id}
|
|
, #{action}
|
|
, #{target}
|
|
, <choose>
|
|
<when test="details_json != null">#{details_json}::jsonb</when>
|
|
<otherwise>NULL</otherwise>
|
|
</choose>
|
|
, COALESCE(#{success}, TRUE)
|
|
, #{error_message}
|
|
, NOW()
|
|
)
|
|
</insert>
|
|
|
|
<select id="selectAuditLog" parameterType="map" resultType="map">
|
|
SELECT
|
|
ID as id
|
|
, COMPANY_CODE as company_code
|
|
, ACTOR_USER_ID as actor_user_id
|
|
, ACTION as action
|
|
, TARGET as target
|
|
, DETAILS as details
|
|
, SUCCESS as success
|
|
, ERROR_MESSAGE as error_message
|
|
, CREATED_AT as created_at
|
|
FROM COMPANY_AUDIT_LOG
|
|
<where>
|
|
<if test="company_code != null">AND COMPANY_CODE = #{company_code}</if>
|
|
<if test="action != null">AND ACTION = #{action}</if>
|
|
</where>
|
|
ORDER BY CREATED_AT DESC, ID DESC
|
|
LIMIT #{limit} OFFSET #{offset}
|
|
</select>
|
|
|
|
<select id="countAuditLog" parameterType="map" resultType="long">
|
|
SELECT COUNT(*) FROM COMPANY_AUDIT_LOG
|
|
<where>
|
|
<if test="company_code != null">AND COMPANY_CODE = #{company_code}</if>
|
|
<if test="action != null">AND ACTION = #{action}</if>
|
|
</where>
|
|
</select>
|
|
|
|
<!--
|
|
Phase A (cross-tenant 집계, 2026-04-27): SUPER_ADMIN fan-out 대상 회사 목록.
|
|
listCompaniesForUi 와 다름 — 그 쿼리는 회사관리 화면 렌더용으로 모든 상태 포함.
|
|
이건 active 상태만, 라우팅 가능한(DB_NAME 박힌) 회사만.
|
|
-->
|
|
<select id="listActiveCompanies" resultType="map">
|
|
SELECT
|
|
COMPANY_CODE AS company_code
|
|
, COMPANY_NAME AS company_name
|
|
, DB_NAME AS db_name
|
|
FROM COMPANY_MNG
|
|
WHERE DB_STATUS = 'active'
|
|
AND DB_NAME IS NOT NULL
|
|
ORDER BY COMPANY_CODE
|
|
</select>
|
|
|
|
</mapper>
|