feat(knowledge): add multi-space platform schema

This commit is contained in:
2026-07-16 10:21:35 +08:00
parent 5a131b57ef
commit 735e9a78c1
3 changed files with 281 additions and 6 deletions
@@ -0,0 +1,44 @@
package org.dromara.aihr.service;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
@Tag("dev")
class AihrKnowledgePlatformSchemaTest {
@Test
void schemaDefinesSpacesAppsGrantsAndAuditWithoutPlainTokens() throws Exception {
String sql = Files.readString(repoRoot().resolve("backend/script/sql/aihr_knowledge_mysql8.sql"));
String migration = Files.readString(repoRoot().resolve(
"backend/script/sql/update/aihr_20260716_knowledge_space_platform_mysql8.sql"));
String schema = sql + migration;
assertTrue(schema.contains("`code` varchar(100)"));
assertTrue(schema.contains("uk_aihr_knowledge_info_code"));
assertTrue(schema.contains("aihr_knowledge_space_grant"));
assertTrue(schema.contains("aihr_knowledge_app"));
assertTrue(schema.contains("aihr_knowledge_app_space"));
assertTrue(schema.contains("aihr_knowledge_query_log"));
assertTrue(schema.contains("space_codes_json"));
assertTrue(schema.contains("token_hash"));
assertFalse(schema.contains("token_plain"));
assertFalse(schema.contains("aihr_personal_"));
}
private static Path repoRoot() {
Path current = Path.of("").toAbsolutePath();
while (current != null && !Files.exists(current.resolve("backend/script/sql"))) {
current = current.getParent();
}
if (current == null) {
throw new IllegalStateException("repository root not found");
}
return current;
}
}
+83 -6
View File
@@ -5,6 +5,10 @@ CREATE TABLE IF NOT EXISTS `aihr_knowledge_info` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` varchar(20) DEFAULT '000000' COMMENT '租户编号',
`name` varchar(100) NOT NULL COMMENT '知识库名称',
`code` varchar(100) NOT NULL COMMENT '租户内稳定空间编码',
`space_type` varchar(30) NOT NULL DEFAULT 'BUSINESS' COMMENT 'PUBLIC/BUSINESS/MANAGEMENT/EXTERNAL',
`sensitivity_level` varchar(30) NOT NULL DEFAULT 'INTERNAL' COMMENT 'PUBLIC/INTERNAL/CONFIDENTIAL',
`status` varchar(20) NOT NULL DEFAULT 'ACTIVE' COMMENT 'DRAFT/ACTIVE/DISABLED',
`description` varchar(1000) DEFAULT NULL COMMENT '知识库描述',
`separator` varchar(255) DEFAULT NULL COMMENT '知识分隔符',
`overlap_char` int DEFAULT 120 COMMENT '重叠字符数',
@@ -23,6 +27,7 @@ CREATE TABLE IF NOT EXISTS `aihr_knowledge_info` (
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
`remark` varchar(500) DEFAULT NULL COMMENT '备注',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_knowledge_info_code` (`tenant_id`, `code`),
KEY `idx_aihr_knowledge_info_tenant` (`tenant_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI HR 知识库';
@@ -89,13 +94,13 @@ CREATE TABLE IF NOT EXISTS `aihr_knowledge_import_task` (
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI HR 知识库导入任务';
INSERT INTO `aihr_knowledge_info`
(`id`, `tenant_id`, `name`, `description`, `separator`, `overlap_char`, `retrieve_limit`, `similarity_threshold`, `text_block_size`, `vector_model`, `embedding_model`, `enable_hybrid`, `hybrid_alpha`, `system_prompt`, `create_time`, `update_time`, `remark`)
(`id`, `tenant_id`, `name`, `code`, `description`, `separator`, `overlap_char`, `retrieve_limit`, `similarity_threshold`, `text_block_size`, `vector_model`, `embedding_model`, `enable_hybrid`, `hybrid_alpha`, `system_prompt`, `create_time`, `update_time`, `remark`)
VALUES
(1001, '000000', '投诉处理 SOP', '业主投诉的接待、记录、派单、反馈和闭环确认。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须引用投诉处理 SOP 片段,不得编造未命中的金额、责任和时限。', NOW(), NOW(), 'seed:sop'),
(1002, '000000', '催缴沟通 SOP', '缴费提醒、费用争议回应、服务问题承接。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须区分服务整改与费用义务。', NOW(), NOW(), 'seed:sop'),
(1003, '000000', '报修跟进 SOP', '报修接单、派工、现场反馈、复核关闭。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须给出明确节点和责任人。', NOW(), NOW(), 'seed:sop'),
(1201, '000000', '增值服务需求识别 SOP', '基于业主真实需求介绍增值服务,明确适用范围、价格和取消规则。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '只能基于已确认的服务目录和价目口径回答,不得编造优惠或效果承诺。', NOW(), NOW(), 'seed:sop:brd-supplement'),
(1202, '000000', '日常服务回访 SOP', '服务完成后的满意度确认、遗留问题记录、责任人和回访节点。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '已完成不等于已闭环,回答必须包含满意度确认、责任人和下一次回访节点。', NOW(), NOW(), 'seed:sop:brd-supplement')
(1001, '000000', '投诉处理 SOP', 'legacy_1001', '业主投诉的接待、记录、派单、反馈和闭环确认。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须引用投诉处理 SOP 片段,不得编造未命中的金额、责任和时限。', NOW(), NOW(), 'seed:sop'),
(1002, '000000', '催缴沟通 SOP', 'legacy_1002', '缴费提醒、费用争议回应、服务问题承接。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须区分服务整改与费用义务。', NOW(), NOW(), 'seed:sop'),
(1003, '000000', '报修跟进 SOP', 'legacy_1003', '报修接单、派工、现场反馈、复核关闭。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '回答必须给出明确节点和责任人。', NOW(), NOW(), 'seed:sop'),
(1201, '000000', '增值服务需求识别 SOP', 'legacy_1201', '基于业主真实需求介绍增值服务,明确适用范围、价格和取消规则。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '只能基于已确认的服务目录和价目口径回答,不得编造优惠或效果承诺。', NOW(), NOW(), 'seed:sop:brd-supplement'),
(1202, '000000', '日常服务回访 SOP', 'legacy_1202', '服务完成后的满意度确认、遗留问题记录、责任人和回访节点。', '\n\n', 120, 5, 0.5, 800, NULL, NULL, 1, 1.0, '已完成不等于已闭环,回答必须包含满意度确认、责任人和下一次回访节点。', NOW(), NOW(), 'seed:sop:brd-supplement')
ON DUPLICATE KEY UPDATE
`description` = VALUES(`description`),
`system_prompt` = VALUES(`system_prompt`),
@@ -142,6 +147,7 @@ CREATE TABLE IF NOT EXISTS `aihr_knowledge_upload_item` (
`batch_id` varchar(64) NOT NULL COMMENT '上传批次',
`file_name` varchar(255) NOT NULL COMMENT '文件名',
`category` varchar(100) DEFAULT '' COMMENT '目标分类',
`space_codes_json` json DEFAULT NULL COMMENT '目标知识空间编码数组',
`staging_path` varchar(500) NOT NULL COMMENT '暂存文件路径',
`status` tinyint NOT NULL DEFAULT 0 COMMENT '0待处理 1处理中 2完成 3失败',
`error` varchar(500) DEFAULT NULL COMMENT '失败原因',
@@ -153,3 +159,74 @@ CREATE TABLE IF NOT EXISTS `aihr_knowledge_upload_item` (
KEY `idx_aihr_upload_item_batch` (`tenant_id`, `batch_id`, `id`),
KEY `idx_aihr_upload_item_status` (`tenant_id`, `status`, `update_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='知识库批量上传队列';
CREATE TABLE IF NOT EXISTS `aihr_knowledge_space_grant` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`knowledge_id` bigint NOT NULL COMMENT '知识空间ID',
`principal_type` varchar(20) NOT NULL COMMENT 'ROLE/USER',
`principal_value` varchar(100) NOT NULL COMMENT '角色编码或用户ID',
`permission` varchar(20) NOT NULL COMMENT 'READ/MANAGE',
`status` varchar(20) NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE/DISABLED',
`create_by` bigint DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_space_grant` (`tenant_id`, `knowledge_id`, `principal_type`, `principal_value`, `permission`),
KEY `idx_aihr_space_grant_principal` (`tenant_id`, `principal_type`, `principal_value`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='知识空间主体授权';
CREATE TABLE IF NOT EXISTS `aihr_knowledge_app` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`app_code` varchar(64) NOT NULL COMMENT '调用应用稳定编码',
`app_name` varchar(100) NOT NULL COMMENT '调用应用名称',
`auth_type` varchar(20) NOT NULL COMMENT 'SESSION/API_TOKEN',
`internal_client_key` varchar(64) DEFAULT NULL COMMENT '内部登录客户端key',
`token_hash` varchar(64) DEFAULT NULL COMMENT '外部令牌SHA-256',
`status` varchar(20) NOT NULL DEFAULT 'ACTIVE' COMMENT 'ACTIVE/DISABLED',
`rate_limit_per_minute` int NOT NULL DEFAULT 60 COMMENT '每分钟请求上限',
`expires_time` datetime DEFAULT NULL COMMENT '令牌到期时间',
`last_used_time` datetime DEFAULT NULL COMMENT '最后调用时间',
`create_by` bigint DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_by` bigint DEFAULT NULL COMMENT '更新者',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_knowledge_app_code` (`app_code`),
UNIQUE KEY `uk_aihr_knowledge_app_client` (`tenant_id`, `auth_type`, `internal_client_key`),
KEY `idx_aihr_knowledge_app_tenant_status` (`tenant_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='知识问答调用应用';
CREATE TABLE IF NOT EXISTS `aihr_knowledge_app_space` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`app_id` bigint NOT NULL COMMENT '调用应用ID',
`knowledge_id` bigint NOT NULL COMMENT '知识空间ID',
`create_by` bigint DEFAULT NULL COMMENT '创建者',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_app_space` (`tenant_id`, `app_id`, `knowledge_id`),
KEY `idx_aihr_app_space_knowledge` (`tenant_id`, `knowledge_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调用应用与知识空间授权';
CREATE TABLE IF NOT EXISTS `aihr_knowledge_query_log` (
`id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
`request_id` varchar(64) NOT NULL COMMENT '请求追踪ID',
`tenant_id` varchar(20) NOT NULL COMMENT '租户编号',
`app_id` bigint NOT NULL COMMENT '调用应用ID',
`user_id` bigint DEFAULT NULL COMMENT '内部用户ID',
`question_hash` varchar(64) NOT NULL COMMENT '问题SHA-256',
`used_space_codes_json` json DEFAULT NULL COMMENT '实际使用空间编码',
`source_types_json` json DEFAULT NULL COMMENT '来源类型',
`status` varchar(30) NOT NULL COMMENT 'SUCCESS/NO_EVIDENCE/REJECTED/FAILED',
`latency_ms` bigint NOT NULL DEFAULT 0 COMMENT '处理耗时毫秒',
`model_name` varchar(100) DEFAULT NULL COMMENT '生成模型',
`prompt_version` varchar(100) DEFAULT NULL COMMENT '提示词版本',
`create_time` datetime NOT NULL COMMENT '创建时间',
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_query_request` (`request_id`),
KEY `idx_aihr_query_tenant_app` (`tenant_id`, `app_id`, `create_time`),
KEY `idx_aihr_query_status` (`tenant_id`, `status`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='知识问答最小化审计日志';
@@ -0,0 +1,154 @@
-- AIHR 多租户多知识空间统一问答平台迁移(MySQL 8.x)。
-- 只补结构和 legacy 编码,不移动既有附件、片段或 OSS 数据;可重复执行。
SET @aihr_space_code_exists := (
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_knowledge_info' AND COLUMN_NAME = 'code'
);
SET @aihr_space_code_ddl := IF(
@aihr_space_code_exists = 0,
'ALTER TABLE aihr_knowledge_info ADD COLUMN `code` varchar(100) DEFAULT NULL COMMENT ''租户内稳定空间编码'' AFTER `name`',
'SELECT 1'
);
PREPARE aihr_space_code_stmt FROM @aihr_space_code_ddl;
EXECUTE aihr_space_code_stmt;
DEALLOCATE PREPARE aihr_space_code_stmt;
UPDATE aihr_knowledge_info SET code = CONCAT('legacy_', id) WHERE code IS NULL OR code = '';
ALTER TABLE aihr_knowledge_info MODIFY COLUMN `code` varchar(100) NOT NULL COMMENT '租户内稳定空间编码';
SET @aihr_space_type_exists := (
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_knowledge_info' AND COLUMN_NAME = 'space_type'
);
SET @aihr_space_type_ddl := IF(
@aihr_space_type_exists = 0,
'ALTER TABLE aihr_knowledge_info ADD COLUMN `space_type` varchar(30) NOT NULL DEFAULT ''BUSINESS'' COMMENT ''PUBLIC/BUSINESS/MANAGEMENT/EXTERNAL'' AFTER `code`',
'SELECT 1'
);
PREPARE aihr_space_type_stmt FROM @aihr_space_type_ddl;
EXECUTE aihr_space_type_stmt;
DEALLOCATE PREPARE aihr_space_type_stmt;
SET @aihr_space_sensitivity_exists := (
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_knowledge_info' AND COLUMN_NAME = 'sensitivity_level'
);
SET @aihr_space_sensitivity_ddl := IF(
@aihr_space_sensitivity_exists = 0,
'ALTER TABLE aihr_knowledge_info ADD COLUMN `sensitivity_level` varchar(30) NOT NULL DEFAULT ''INTERNAL'' COMMENT ''PUBLIC/INTERNAL/CONFIDENTIAL'' AFTER `space_type`',
'SELECT 1'
);
PREPARE aihr_space_sensitivity_stmt FROM @aihr_space_sensitivity_ddl;
EXECUTE aihr_space_sensitivity_stmt;
DEALLOCATE PREPARE aihr_space_sensitivity_stmt;
SET @aihr_space_status_exists := (
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_knowledge_info' AND COLUMN_NAME = 'status'
);
SET @aihr_space_status_ddl := IF(
@aihr_space_status_exists = 0,
'ALTER TABLE aihr_knowledge_info ADD COLUMN `status` varchar(20) NOT NULL DEFAULT ''ACTIVE'' COMMENT ''DRAFT/ACTIVE/DISABLED'' AFTER `sensitivity_level`',
'SELECT 1'
);
PREPARE aihr_space_status_stmt FROM @aihr_space_status_ddl;
EXECUTE aihr_space_status_stmt;
DEALLOCATE PREPARE aihr_space_status_stmt;
SET @aihr_space_code_index_exists := (
SELECT COUNT(*) FROM information_schema.STATISTICS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_knowledge_info' AND INDEX_NAME = 'uk_aihr_knowledge_info_code'
);
SET @aihr_space_code_index_ddl := IF(
@aihr_space_code_index_exists = 0,
'ALTER TABLE aihr_knowledge_info ADD UNIQUE KEY `uk_aihr_knowledge_info_code` (`tenant_id`, `code`)',
'SELECT 1'
);
PREPARE aihr_space_code_index_stmt FROM @aihr_space_code_index_ddl;
EXECUTE aihr_space_code_index_stmt;
DEALLOCATE PREPARE aihr_space_code_index_stmt;
SET @aihr_upload_spaces_exists := (
SELECT COUNT(*) FROM information_schema.COLUMNS
WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = 'aihr_knowledge_upload_item' AND COLUMN_NAME = 'space_codes_json'
);
SET @aihr_upload_spaces_ddl := IF(
@aihr_upload_spaces_exists = 0,
'ALTER TABLE aihr_knowledge_upload_item ADD COLUMN `space_codes_json` json DEFAULT NULL COMMENT ''目标知识空间编码数组'' AFTER `category`',
'SELECT 1'
);
PREPARE aihr_upload_spaces_stmt FROM @aihr_upload_spaces_ddl;
EXECUTE aihr_upload_spaces_stmt;
DEALLOCATE PREPARE aihr_upload_spaces_stmt;
CREATE TABLE IF NOT EXISTS `aihr_knowledge_space_grant` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`knowledge_id` bigint NOT NULL,
`principal_type` varchar(20) NOT NULL,
`principal_value` varchar(100) NOT NULL,
`permission` varchar(20) NOT NULL,
`status` varchar(20) NOT NULL DEFAULT 'ACTIVE',
`create_by` bigint DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_by` bigint DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_space_grant` (`tenant_id`, `knowledge_id`, `principal_type`, `principal_value`, `permission`),
KEY `idx_aihr_space_grant_principal` (`tenant_id`, `principal_type`, `principal_value`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='知识空间主体授权';
CREATE TABLE IF NOT EXISTS `aihr_knowledge_app` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`app_code` varchar(64) NOT NULL,
`app_name` varchar(100) NOT NULL,
`auth_type` varchar(20) NOT NULL,
`internal_client_key` varchar(64) DEFAULT NULL,
`token_hash` varchar(64) DEFAULT NULL,
`status` varchar(20) NOT NULL DEFAULT 'ACTIVE',
`rate_limit_per_minute` int NOT NULL DEFAULT 60,
`expires_time` datetime DEFAULT NULL,
`last_used_time` datetime DEFAULT NULL,
`create_by` bigint DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
`update_by` bigint DEFAULT NULL,
`update_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_knowledge_app_code` (`app_code`),
UNIQUE KEY `uk_aihr_knowledge_app_client` (`tenant_id`, `auth_type`, `internal_client_key`),
KEY `idx_aihr_knowledge_app_tenant_status` (`tenant_id`, `status`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='知识问答调用应用';
CREATE TABLE IF NOT EXISTS `aihr_knowledge_app_space` (
`id` bigint NOT NULL AUTO_INCREMENT,
`tenant_id` varchar(20) NOT NULL,
`app_id` bigint NOT NULL,
`knowledge_id` bigint NOT NULL,
`create_by` bigint DEFAULT NULL,
`create_time` datetime DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_app_space` (`tenant_id`, `app_id`, `knowledge_id`),
KEY `idx_aihr_app_space_knowledge` (`tenant_id`, `knowledge_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='调用应用与知识空间授权';
CREATE TABLE IF NOT EXISTS `aihr_knowledge_query_log` (
`id` bigint NOT NULL AUTO_INCREMENT,
`request_id` varchar(64) NOT NULL,
`tenant_id` varchar(20) NOT NULL,
`app_id` bigint NOT NULL,
`user_id` bigint DEFAULT NULL,
`question_hash` varchar(64) NOT NULL,
`used_space_codes_json` json DEFAULT NULL,
`source_types_json` json DEFAULT NULL,
`status` varchar(30) NOT NULL,
`latency_ms` bigint NOT NULL DEFAULT 0,
`model_name` varchar(100) DEFAULT NULL,
`prompt_version` varchar(100) DEFAULT NULL,
`create_time` datetime NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_aihr_query_request` (`request_id`),
KEY `idx_aihr_query_tenant_app` (`tenant_id`, `app_id`, `create_time`),
KEY `idx_aihr_query_status` (`tenant_id`, `status`, `create_time`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='知识问答最小化审计日志';