docs(aicharacter): 크리에이터 연결 계획을 추가한다
This commit is contained in:
235
docs/20260611_AI캐릭터_크리에이터기능_최소연결/alter-existing-tables.sql
Normal file
235
docs/20260611_AI캐릭터_크리에이터기능_최소연결/alter-existing-tables.sql
Normal file
@@ -0,0 +1,235 @@
|
||||
-- AI 캐릭터 크리에이터 기능 최소 연결 운영 DB 반영 SQL
|
||||
-- MySQL 기준. 운영 반영 전 백업과 트랜잭션/락 영향을 점검한다.
|
||||
|
||||
-- 1. member.member_kind 추가
|
||||
SET @member_kind_column_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'member'
|
||||
AND column_name = 'member_kind'
|
||||
);
|
||||
|
||||
SET @add_member_kind_sql := IF(
|
||||
@member_kind_column_exists = 0,
|
||||
'ALTER TABLE member ADD COLUMN member_kind VARCHAR(30) NOT NULL DEFAULT ''HUMAN'' COMMENT ''Member 주체 종류: HUMAN, AI_CHARACTER'' AFTER role',
|
||||
'SELECT ''member.member_kind already exists'' AS message'
|
||||
);
|
||||
|
||||
PREPARE add_member_kind_stmt FROM @add_member_kind_sql;
|
||||
EXECUTE add_member_kind_stmt;
|
||||
DEALLOCATE PREPARE add_member_kind_stmt;
|
||||
|
||||
-- 2. chat_character.creator_member_id nullable 컬럼 추가
|
||||
SET @creator_member_column_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'chat_character'
|
||||
AND column_name = 'creator_member_id'
|
||||
);
|
||||
|
||||
SET @add_creator_member_sql := IF(
|
||||
@creator_member_column_exists = 0,
|
||||
'ALTER TABLE chat_character ADD COLUMN creator_member_id BIGINT NULL COMMENT ''크리에이터 기능 주체 Member ID''',
|
||||
'SELECT ''chat_character.creator_member_id already exists'' AS message'
|
||||
);
|
||||
|
||||
PREPARE add_creator_member_stmt FROM @add_creator_member_sql;
|
||||
EXECUTE add_creator_member_stmt;
|
||||
DEALLOCATE PREPARE add_creator_member_stmt;
|
||||
|
||||
-- 3. 기존 chat_character별 AI 캐릭터용 Member 생성 및 매핑
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_chat_character_creator_member;
|
||||
CREATE TEMPORARY TABLE tmp_chat_character_creator_member (
|
||||
chat_character_id BIGINT NOT NULL PRIMARY KEY,
|
||||
creator_member_id BIGINT NULL
|
||||
) COMMENT 'chat_character와 backfill member.id 임시 매핑';
|
||||
|
||||
INSERT INTO tmp_chat_character_creator_member (chat_character_id)
|
||||
SELECT c.id
|
||||
FROM chat_character c
|
||||
WHERE c.creator_member_id IS NULL;
|
||||
|
||||
DROP PROCEDURE IF EXISTS backfill_chat_character_creator_member;
|
||||
|
||||
DELIMITER //
|
||||
CREATE PROCEDURE backfill_chat_character_creator_member()
|
||||
BEGIN
|
||||
DECLARE done BOOLEAN DEFAULT FALSE;
|
||||
DECLARE v_chat_character_id BIGINT;
|
||||
DECLARE v_name VARCHAR(255);
|
||||
DECLARE v_description TEXT;
|
||||
DECLARE v_image_path VARCHAR(255);
|
||||
|
||||
DECLARE character_cursor CURSOR FOR
|
||||
SELECT c.id, c.name, c.description, c.image_path
|
||||
FROM chat_character c
|
||||
INNER JOIN tmp_chat_character_creator_member m
|
||||
ON m.chat_character_id = c.id
|
||||
WHERE m.creator_member_id IS NULL
|
||||
ORDER BY c.id;
|
||||
|
||||
DECLARE CONTINUE HANDLER FOR NOT FOUND SET done = TRUE;
|
||||
|
||||
OPEN character_cursor;
|
||||
|
||||
read_loop: LOOP
|
||||
FETCH character_cursor INTO v_chat_character_id, v_name, v_description, v_image_path;
|
||||
|
||||
IF done THEN
|
||||
LEAVE read_loop;
|
||||
END IF;
|
||||
|
||||
INSERT INTO member (
|
||||
email,
|
||||
password,
|
||||
nickname,
|
||||
profile_image,
|
||||
provider,
|
||||
gender,
|
||||
role,
|
||||
member_kind,
|
||||
is_visible_donation_rank,
|
||||
donation_ranking_period,
|
||||
is_active,
|
||||
container,
|
||||
introduce,
|
||||
instagram_url,
|
||||
fancimm_url,
|
||||
x_url,
|
||||
youtube_url,
|
||||
website_url,
|
||||
blog_url,
|
||||
pg_charge_can,
|
||||
pg_reward_can,
|
||||
google_charge_can,
|
||||
google_reward_can,
|
||||
apple_charge_can,
|
||||
apple_reward_can,
|
||||
created_at,
|
||||
updated_at
|
||||
) VALUES (
|
||||
NULL,
|
||||
'',
|
||||
v_name,
|
||||
v_image_path,
|
||||
'EMAIL',
|
||||
'NONE',
|
||||
'CREATOR',
|
||||
'AI_CHARACTER',
|
||||
TRUE,
|
||||
'CUMULATIVE',
|
||||
TRUE,
|
||||
'web',
|
||||
COALESCE(v_description, ''),
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
'',
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
CURRENT_TIMESTAMP,
|
||||
CURRENT_TIMESTAMP
|
||||
);
|
||||
|
||||
UPDATE tmp_chat_character_creator_member
|
||||
SET creator_member_id = LAST_INSERT_ID()
|
||||
WHERE chat_character_id = v_chat_character_id;
|
||||
END LOOP;
|
||||
|
||||
CLOSE character_cursor;
|
||||
END //
|
||||
DELIMITER ;
|
||||
|
||||
CALL backfill_chat_character_creator_member();
|
||||
DROP PROCEDURE IF EXISTS backfill_chat_character_creator_member;
|
||||
|
||||
UPDATE chat_character c
|
||||
INNER JOIN tmp_chat_character_creator_member m
|
||||
ON m.chat_character_id = c.id
|
||||
SET c.creator_member_id = m.creator_member_id
|
||||
WHERE c.creator_member_id IS NULL
|
||||
AND m.creator_member_id IS NOT NULL;
|
||||
|
||||
-- 4. unique index 추가
|
||||
SET @creator_member_unique_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.statistics
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'chat_character'
|
||||
AND index_name = 'uk_chat_character_creator_member'
|
||||
);
|
||||
|
||||
SET @add_creator_member_unique_sql := IF(
|
||||
@creator_member_unique_exists = 0,
|
||||
'ALTER TABLE chat_character ADD UNIQUE INDEX uk_chat_character_creator_member (creator_member_id)',
|
||||
'SELECT ''uk_chat_character_creator_member already exists'' AS message'
|
||||
);
|
||||
|
||||
PREPARE add_creator_member_unique_stmt FROM @add_creator_member_unique_sql;
|
||||
EXECUTE add_creator_member_unique_stmt;
|
||||
DEALLOCATE PREPARE add_creator_member_unique_stmt;
|
||||
|
||||
-- 5. foreign key 추가
|
||||
SET @creator_member_fk_exists := (
|
||||
SELECT COUNT(*)
|
||||
FROM information_schema.table_constraints
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'chat_character'
|
||||
AND constraint_name = 'fk_chat_character_creator_member'
|
||||
AND constraint_type = 'FOREIGN KEY'
|
||||
);
|
||||
|
||||
SET @add_creator_member_fk_sql := IF(
|
||||
@creator_member_fk_exists = 0,
|
||||
'ALTER TABLE chat_character ADD CONSTRAINT fk_chat_character_creator_member FOREIGN KEY (creator_member_id) REFERENCES member (id)',
|
||||
'SELECT ''fk_chat_character_creator_member already exists'' AS message'
|
||||
);
|
||||
|
||||
PREPARE add_creator_member_fk_stmt FROM @add_creator_member_fk_sql;
|
||||
EXECUTE add_creator_member_fk_stmt;
|
||||
DEALLOCATE PREPARE add_creator_member_fk_stmt;
|
||||
|
||||
-- 6. 운영 반영 전 필수 검증. 두 결과 모두 0이어야 한다.
|
||||
SELECT COUNT(*) AS invalid_ai_character_member_count
|
||||
FROM member
|
||||
WHERE member_kind = 'AI_CHARACTER'
|
||||
AND role <> 'CREATOR';
|
||||
|
||||
SELECT COUNT(*) AS missing_creator_member_count
|
||||
FROM chat_character
|
||||
WHERE creator_member_id IS NULL;
|
||||
|
||||
-- 7. 검증 완료 후 creator_member_id NOT NULL 전환
|
||||
SET @missing_creator_member_count := (
|
||||
SELECT COUNT(*)
|
||||
FROM chat_character
|
||||
WHERE creator_member_id IS NULL
|
||||
);
|
||||
|
||||
SET @creator_member_nullable := (
|
||||
SELECT is_nullable
|
||||
FROM information_schema.columns
|
||||
WHERE table_schema = DATABASE()
|
||||
AND table_name = 'chat_character'
|
||||
AND column_name = 'creator_member_id'
|
||||
);
|
||||
|
||||
SET @modify_creator_member_not_null_sql := IF(
|
||||
@missing_creator_member_count = 0 AND @creator_member_nullable = 'YES',
|
||||
'ALTER TABLE chat_character MODIFY COLUMN creator_member_id BIGINT NOT NULL COMMENT ''크리에이터 기능 주체 Member ID''',
|
||||
'SELECT ''chat_character.creator_member_id not modified; verify missing_creator_member_count is 0 and column is nullable'' AS message'
|
||||
);
|
||||
|
||||
PREPARE modify_creator_member_not_null_stmt FROM @modify_creator_member_not_null_sql;
|
||||
EXECUTE modify_creator_member_not_null_stmt;
|
||||
DEALLOCATE PREPARE modify_creator_member_not_null_stmt;
|
||||
|
||||
DROP TEMPORARY TABLE IF EXISTS tmp_chat_character_creator_member;
|
||||
Reference in New Issue
Block a user