feat(personal-assistant): add confirmed project memory

This commit is contained in:
2026-07-19 08:19:24 +08:00
parent 151cc26caf
commit 24da833fa6
31 changed files with 4037 additions and 60 deletions
@@ -0,0 +1,100 @@
-- 个人助理项目服务记忆最小切片:候选确认、项目事实与版本快照。
CREATE TABLE IF NOT EXISTS `aihr_memory_candidate` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '候选ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`owner_user_id` bigint NOT NULL COMMENT '候选所属用户',
`source_conversation_id` varchar(64) NOT NULL DEFAULT '' COMMENT '来源会话ID,无状态问答为空串',
`source_request_id` varchar(64) NOT NULL COMMENT '来源问答请求ID',
`candidate_hash` char(64) NOT NULL COMMENT '规范化候选SHA-256',
`project_code` varchar(100) DEFAULT NULL COMMENT '经当前身份解析的真实项目编码',
`memory_type` varchar(30) NOT NULL COMMENT 'RESIDENT_NEED/FOLLOW_UP',
`target_domain` varchar(30) NOT NULL DEFAULT 'PROJECT_SERVICE' COMMENT '目标记忆域',
`status` varchar(20) NOT NULL COMMENT 'NEEDS_INPUT/DRAFT/CONFIRMED/DISMISSED/EXPIRED',
`draft_json` json DEFAULT NULL COMMENT '可编辑的结构化草稿,不保存模型原始输出',
`missing_fields_json` json DEFAULT NULL COMMENT '待补充字段',
`confidence` decimal(5,4) NOT NULL DEFAULT 0 COMMENT '只用于展示建议',
`target_id` bigint DEFAULT NULL COMMENT '确认后项目记忆ID',
`target_version` int DEFAULT NULL COMMENT '确认结果版本',
`version` int NOT NULL DEFAULT 1 COMMENT '候选乐观锁版本',
`confirm_idempotency_key` varchar(100) DEFAULT NULL COMMENT '确认幂等键',
`confirm_request_hash` char(64) DEFAULT NULL COMMENT '确认请求规范化哈希',
`expires_at` datetime NOT NULL COMMENT '草稿过期时间',
`confirmed_at` datetime DEFAULT NULL COMMENT '确认时间',
`dismissed_at` datetime DEFAULT NULL COMMENT '忽略时间',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_memory_candidate_source` (`tenant_id`, `owner_user_id`, `source_request_id`, `candidate_hash`),
UNIQUE KEY `uk_aihr_memory_candidate_confirm_key` (`tenant_id`, `owner_user_id`, `confirm_idempotency_key`),
KEY `idx_aihr_memory_candidate_owner` (`tenant_id`, `owner_user_id`, `status`, `expires_at`),
KEY `idx_aihr_memory_candidate_expiry` (`status`, `expires_at`),
KEY `idx_aihr_memory_candidate_cleanup` (`status`, `update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='个人助理长期记忆候选';
CREATE TABLE IF NOT EXISTS `aihr_service_memory` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '项目服务记忆ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`project_code` varchar(100) NOT NULL COMMENT '真实项目编码',
`memory_type` varchar(30) NOT NULL COMMENT 'RESIDENT_NEED/FOLLOW_UP',
`subject_type` varchar(20) NOT NULL DEFAULT 'LOCATION' COMMENT '当前无权威住户主数据,仅定位到房屋',
`building_name` varchar(100) NOT NULL COMMENT '楼栋',
`unit_name` varchar(100) NOT NULL COMMENT '单元',
`room_no` varchar(100) NOT NULL COMMENT '房号',
`category` varchar(50) NOT NULL COMMENT '需求分类',
`title` varchar(500) NOT NULL COMMENT '标题',
`summary` varchar(1000) NOT NULL COMMENT '最小必要摘要',
`detail` text COMMENT '必要说明',
`status` varchar(20) NOT NULL DEFAULT 'OPEN' COMMENT 'OPEN/IN_PROGRESS/RESOLVED/VOID',
`occurred_at` datetime NOT NULL COMMENT '发生或获知时间',
`follow_up_at` datetime DEFAULT NULL COMMENT '用户单独确认的跟进时间',
`source_conversation_id` varchar(64) NOT NULL DEFAULT '' COMMENT '来源会话ID',
`source_request_id` varchar(64) NOT NULL COMMENT '来源问答请求ID',
`created_by` bigint NOT NULL COMMENT '记录人',
`confirmed_by` bigint NOT NULL COMMENT '确认人',
`version` int NOT NULL DEFAULT 1 COMMENT '当前版本',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_aihr_service_memory_scope` (`tenant_id`, `project_code`, `status`, `follow_up_at`),
KEY `idx_aihr_service_memory_location` (`tenant_id`, `project_code`, `building_name`, `unit_name`, `room_no`),
KEY `idx_aihr_service_memory_source` (`tenant_id`, `source_request_id`),
FULLTEXT KEY `ft_aihr_service_memory_text` (`title`, `summary`, `detail`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目共享服务记忆';
CREATE TABLE IF NOT EXISTS `aihr_service_memory_version` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '版本ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`project_code` varchar(100) NOT NULL COMMENT '项目编码快照',
`memory_id` bigint NOT NULL COMMENT '项目服务记忆ID',
`version` int NOT NULL COMMENT '版本号',
`snapshot_json` json NOT NULL COMMENT '业务字段快照',
`change_type` varchar(30) NOT NULL COMMENT 'CONFIRM/UPDATE/STATUS/VOID',
`change_reason` varchar(500) DEFAULT NULL COMMENT '变更原因',
`operator_user_id` bigint NOT NULL COMMENT '操作人',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_service_memory_version` (`memory_id`, `version`),
KEY `idx_aihr_service_memory_version_scope` (`tenant_id`, `project_code`, `memory_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目服务记忆版本快照';
-- 生产旧库与 MySQL 8 新库的排序规则可能不同;以现有租户主表为事实源动态对齐。
SET @aihr_memory_collation := (
SELECT collation_name
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'aihr_knowledge_info'
AND column_name = 'tenant_id'
LIMIT 1
);
SET @aihr_memory_collation := COALESCE(@aihr_memory_collation, @@collation_database);
SET @aihr_memory_ddl := CONCAT('ALTER TABLE `aihr_memory_candidate` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_memory_collation);
PREPARE aihr_memory_stmt FROM @aihr_memory_ddl; EXECUTE aihr_memory_stmt; DEALLOCATE PREPARE aihr_memory_stmt;
SET @aihr_memory_ddl := CONCAT('ALTER TABLE `aihr_service_memory` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_memory_collation);
PREPARE aihr_memory_stmt FROM @aihr_memory_ddl; EXECUTE aihr_memory_stmt; DEALLOCATE PREPARE aihr_memory_stmt;
SET @aihr_memory_ddl := CONCAT('ALTER TABLE `aihr_service_memory_version` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_memory_collation);
PREPARE aihr_memory_stmt FROM @aihr_memory_ddl; EXECUTE aihr_memory_stmt; DEALLOCATE PREPARE aihr_memory_stmt;
SET @aihr_memory_ddl := NULL;
SET @aihr_memory_collation := NULL;
@@ -0,0 +1,128 @@
-- 2026-07-19 项目服务记忆增量迁移。与初始化脚本保持同构,可重复执行。
CREATE TABLE IF NOT EXISTS `aihr_memory_candidate` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '候选ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`owner_user_id` bigint NOT NULL COMMENT '候选所属用户',
`source_conversation_id` varchar(64) NOT NULL DEFAULT '' COMMENT '来源会话ID,无状态问答为空串',
`source_request_id` varchar(64) NOT NULL COMMENT '来源问答请求ID',
`candidate_hash` char(64) NOT NULL COMMENT '规范化候选SHA-256',
`project_code` varchar(100) DEFAULT NULL COMMENT '经当前身份解析的真实项目编码',
`memory_type` varchar(30) NOT NULL COMMENT 'RESIDENT_NEED/FOLLOW_UP',
`target_domain` varchar(30) NOT NULL DEFAULT 'PROJECT_SERVICE' COMMENT '目标记忆域',
`status` varchar(20) NOT NULL COMMENT 'NEEDS_INPUT/DRAFT/CONFIRMED/DISMISSED/EXPIRED',
`draft_json` json DEFAULT NULL COMMENT '可编辑的结构化草稿,不保存模型原始输出',
`missing_fields_json` json DEFAULT NULL COMMENT '待补充字段',
`confidence` decimal(5,4) NOT NULL DEFAULT 0 COMMENT '只用于展示建议',
`target_id` bigint DEFAULT NULL COMMENT '确认后项目记忆ID',
`target_version` int DEFAULT NULL COMMENT '确认结果版本',
`version` int NOT NULL DEFAULT 1 COMMENT '候选乐观锁版本',
`confirm_idempotency_key` varchar(100) DEFAULT NULL COMMENT '确认幂等键',
`confirm_request_hash` char(64) DEFAULT NULL COMMENT '确认请求规范化哈希',
`expires_at` datetime NOT NULL COMMENT '草稿过期时间',
`confirmed_at` datetime DEFAULT NULL COMMENT '确认时间',
`dismissed_at` datetime DEFAULT NULL COMMENT '忽略时间',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_memory_candidate_source` (`tenant_id`, `owner_user_id`, `source_request_id`, `candidate_hash`),
UNIQUE KEY `uk_aihr_memory_candidate_confirm_key` (`tenant_id`, `owner_user_id`, `confirm_idempotency_key`),
KEY `idx_aihr_memory_candidate_owner` (`tenant_id`, `owner_user_id`, `status`, `expires_at`),
KEY `idx_aihr_memory_candidate_expiry` (`status`, `expires_at`),
KEY `idx_aihr_memory_candidate_cleanup` (`status`, `update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='个人助理长期记忆候选';
CREATE TABLE IF NOT EXISTS `aihr_service_memory` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '项目服务记忆ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`project_code` varchar(100) NOT NULL COMMENT '真实项目编码',
`memory_type` varchar(30) NOT NULL COMMENT 'RESIDENT_NEED/FOLLOW_UP',
`subject_type` varchar(20) NOT NULL DEFAULT 'LOCATION' COMMENT '当前无权威住户主数据,仅定位到房屋',
`building_name` varchar(100) NOT NULL COMMENT '楼栋',
`unit_name` varchar(100) NOT NULL COMMENT '单元',
`room_no` varchar(100) NOT NULL COMMENT '房号',
`category` varchar(50) NOT NULL COMMENT '需求分类',
`title` varchar(500) NOT NULL COMMENT '标题',
`summary` varchar(1000) NOT NULL COMMENT '最小必要摘要',
`detail` text COMMENT '必要说明',
`status` varchar(20) NOT NULL DEFAULT 'OPEN' COMMENT 'OPEN/IN_PROGRESS/RESOLVED/VOID',
`occurred_at` datetime NOT NULL COMMENT '发生或获知时间',
`follow_up_at` datetime DEFAULT NULL COMMENT '用户单独确认的跟进时间',
`source_conversation_id` varchar(64) NOT NULL DEFAULT '' COMMENT '来源会话ID',
`source_request_id` varchar(64) NOT NULL COMMENT '来源问答请求ID',
`created_by` bigint NOT NULL COMMENT '记录人',
`confirmed_by` bigint NOT NULL COMMENT '确认人',
`version` int NOT NULL DEFAULT 1 COMMENT '当前版本',
`create_time` datetime NOT NULL COMMENT '创建时间',
`update_time` datetime NOT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
KEY `idx_aihr_service_memory_scope` (`tenant_id`, `project_code`, `status`, `follow_up_at`),
KEY `idx_aihr_service_memory_location` (`tenant_id`, `project_code`, `building_name`, `unit_name`, `room_no`),
KEY `idx_aihr_service_memory_source` (`tenant_id`, `source_request_id`),
FULLTEXT KEY `ft_aihr_service_memory_text` (`title`, `summary`, `detail`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目共享服务记忆';
CREATE TABLE IF NOT EXISTS `aihr_service_memory_version` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '版本ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`project_code` varchar(100) NOT NULL COMMENT '项目编码快照',
`memory_id` bigint NOT NULL COMMENT '项目服务记忆ID',
`version` int NOT NULL COMMENT '版本号',
`snapshot_json` json NOT NULL COMMENT '业务字段快照',
`change_type` varchar(30) NOT NULL COMMENT 'CONFIRM/UPDATE/STATUS/VOID',
`change_reason` varchar(500) DEFAULT NULL COMMENT '变更原因',
`operator_user_id` bigint NOT NULL COMMENT '操作人',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_service_memory_version` (`memory_id`, `version`),
KEY `idx_aihr_service_memory_version_scope` (`tenant_id`, `project_code`, `memory_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='项目服务记忆版本快照';
SET @aihr_memory_ddl := IF(
EXISTS(
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_memory_candidate'
AND index_name = 'idx_aihr_memory_candidate_expiry'
),
'SELECT 1',
'ALTER TABLE `aihr_memory_candidate` ADD KEY `idx_aihr_memory_candidate_expiry` (`status`, `expires_at`)'
);
PREPARE aihr_memory_stmt FROM @aihr_memory_ddl; EXECUTE aihr_memory_stmt; DEALLOCATE PREPARE aihr_memory_stmt;
SET @aihr_memory_ddl := IF(
EXISTS(
SELECT 1 FROM information_schema.statistics
WHERE table_schema = DATABASE() AND table_name = 'aihr_memory_candidate'
AND index_name = 'idx_aihr_memory_candidate_cleanup'
),
'SELECT 1',
'ALTER TABLE `aihr_memory_candidate` ADD KEY `idx_aihr_memory_candidate_cleanup` (`status`, `update_time`)'
);
PREPARE aihr_memory_stmt FROM @aihr_memory_ddl; EXECUTE aihr_memory_stmt; DEALLOCATE PREPARE aihr_memory_stmt;
-- 清理早于本迁移产生的终态候选正文;幂等键与来源 ID 保留用于防重。
UPDATE `aihr_memory_candidate`
SET `draft_json` = NULL, `missing_fields_json` = NULL
WHERE `status` IN ('CONFIRMED', 'DISMISSED', 'EXPIRED')
AND `draft_json` IS NOT NULL
AND `update_time` <= DATE_SUB(NOW(), INTERVAL 24 HOUR);
SET @aihr_memory_collation := (
SELECT collation_name
FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'aihr_knowledge_info'
AND column_name = 'tenant_id'
LIMIT 1
);
SET @aihr_memory_collation := COALESCE(@aihr_memory_collation, @@collation_database);
SET @aihr_memory_ddl := CONCAT('ALTER TABLE `aihr_memory_candidate` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_memory_collation);
PREPARE aihr_memory_stmt FROM @aihr_memory_ddl; EXECUTE aihr_memory_stmt; DEALLOCATE PREPARE aihr_memory_stmt;
SET @aihr_memory_ddl := CONCAT('ALTER TABLE `aihr_service_memory` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_memory_collation);
PREPARE aihr_memory_stmt FROM @aihr_memory_ddl; EXECUTE aihr_memory_stmt; DEALLOCATE PREPARE aihr_memory_stmt;
SET @aihr_memory_ddl := CONCAT('ALTER TABLE `aihr_service_memory_version` CONVERT TO CHARACTER SET utf8mb4 COLLATE ', @aihr_memory_collation);
PREPARE aihr_memory_stmt FROM @aihr_memory_ddl; EXECUTE aihr_memory_stmt; DEALLOCATE PREPARE aihr_memory_stmt;
SET @aihr_memory_ddl := NULL;
SET @aihr_memory_collation := NULL;