diff --git a/qiming-backend/sql/update-20260701.sql b/qiming-backend/sql/update-20260701.sql new file mode 100644 index 00000000..daf6dd43 --- /dev/null +++ b/qiming-backend/sql/update-20260701.sql @@ -0,0 +1,273 @@ +-- Qiming backend upgrade SQL for the 2026-07 open-source server increment. +-- +-- Preconditions: +-- 1. Execute after the existing migration chain, including update-20260602.sql. +-- 2. Target database is MySQL-compatible and supports InnoDB JSON columns. +-- 3. The FULLTEXT index on published uses WITH PARSER ngram; verify the target +-- MySQL deployment has the ngram full-text parser enabled before running. +-- +-- This script is written to be repeatable for schema objects added here: +-- tables use IF NOT EXISTS, and columns/indexes are guarded by information_schema. + +DELIMITER $$ + +DROP PROCEDURE IF EXISTS qiming_add_column_if_not_exists$$ +CREATE PROCEDURE qiming_add_column_if_not_exists( + IN p_table_name VARCHAR(64), + IN p_column_name VARCHAR(64), + IN p_alter_sql TEXT +) +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = p_table_name + ) AND NOT EXISTS ( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = p_table_name + AND COLUMN_NAME = p_column_name + ) THEN + SET @qiming_sql = p_alter_sql; + PREPARE qiming_stmt FROM @qiming_sql; + EXECUTE qiming_stmt; + DEALLOCATE PREPARE qiming_stmt; + END IF; +END$$ + +DROP PROCEDURE IF EXISTS qiming_add_index_if_not_exists$$ +CREATE PROCEDURE qiming_add_index_if_not_exists( + IN p_table_name VARCHAR(64), + IN p_index_name VARCHAR(64), + IN p_alter_sql TEXT +) +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.TABLES + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = p_table_name + ) AND NOT EXISTS ( + SELECT 1 + FROM information_schema.STATISTICS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = p_table_name + AND INDEX_NAME = p_index_name + ) THEN + SET @qiming_sql = p_alter_sql; + PREPARE qiming_stmt FROM @qiming_sql; + EXECUTE qiming_stmt; + DEALLOCATE PREPARE qiming_stmt; + END IF; +END$$ + +DROP PROCEDURE IF EXISTS qiming_modify_column_if_exists$$ +CREATE PROCEDURE qiming_modify_column_if_exists( + IN p_table_name VARCHAR(64), + IN p_column_name VARCHAR(64), + IN p_alter_sql TEXT +) +BEGIN + IF EXISTS ( + SELECT 1 + FROM information_schema.COLUMNS + WHERE TABLE_SCHEMA = DATABASE() + AND TABLE_NAME = p_table_name + AND COLUMN_NAME = p_column_name + ) THEN + SET @qiming_sql = p_alter_sql; + PREPARE qiming_stmt FROM @qiming_sql; + EXECUTE qiming_stmt; + DEALLOCATE PREPARE qiming_stmt; + END IF; +END$$ + +DELIMITER ; + +CREATE TABLE IF NOT EXISTS `cm_content` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `_tenant_id` BIGINT NOT NULL DEFAULT 1 COMMENT '租户ID', + `title` VARCHAR(256) NOT NULL COMMENT '标题', + `content` MediumText COMMENT '内容', + `media_url` VARCHAR(512) COMMENT '多媒体链接地址', + `is_external_link` TINYINT NOT NULL DEFAULT 0 COMMENT '是否为外链', + `external_link_url` VARCHAR(512) COMMENT '外链地址', + `is_carousel` TINYINT NOT NULL DEFAULT 0 COMMENT '是否轮播展示', + `category_id` BIGINT COMMENT '分类ID', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + `is_home_show` TINYINT DEFAULT 0 COMMENT '首页展示', + PRIMARY KEY (`id`), + KEY `idx_carousel` (`_tenant_id`, `is_carousel`), + KEY `idx_category` (`_tenant_id`, `category_id`), + KEY `idx_tenant` (`_tenant_id`) +); + +CREATE TABLE IF NOT EXISTS `cm_category` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `_tenant_id` BIGINT NOT NULL DEFAULT 1 COMMENT '租户ID', + `name` VARCHAR(128) NOT NULL COMMENT '分类名称', + `code` VARCHAR(64) NOT NULL COMMENT '分类编码', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + UNIQUE KEY `idx_tenant_code` (`_tenant_id`, `code`), + KEY `idx_tenant` (`_tenant_id`) +); + +CREATE TABLE IF NOT EXISTS `target_recommend` +( + `id` BIGINT NOT NULL AUTO_INCREMENT, + `_tenant_id` BIGINT NOT NULL DEFAULT 1 COMMENT '商户ID', + `target_type` VARCHAR(32) NOT NULL COMMENT '目标类型: Agent, PageApp, Skill, Plugin, Workflow', + `target_id` BIGINT NOT NULL COMMENT '目标对象ID', + `rec_type` VARCHAR(32) NOT NULL COMMENT '推荐类型: Home, Official, ChatBoxNav', + `function_type` VARCHAR(32) COMMENT '功能类型: AgentDev, PageAppDev, SkillDev, PluginDev, Chat', + `label` VARCHAR(128) COMMENT '标签名称', + `icon` VARCHAR(512) COMMENT '图标', + `placeholder` TEXT COMMENT '默认内容, JSON数组格式', + `sort` INT NOT NULL DEFAULT 0 COMMENT '排序', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, + PRIMARY KEY (`id`), + KEY `idx_tenant_rec_type` (`_tenant_id`, `rec_type`) +); + +CREATE TABLE IF NOT EXISTS `eco_market_import_record` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `_tenant_id` BIGINT NOT NULL COMMENT '租户ID', + `user_id` BIGINT NOT NULL COMMENT '用户ID', + `space_id` BIGINT NOT NULL COMMENT '空间ID', + `target_type` VARCHAR(50) NOT NULL COMMENT '目标类型', + `target_id` BIGINT NOT NULL COMMENT '目标ID', + `eco_target_id` VARCHAR(32) NOT NULL COMMENT '生态市场对应目标ID', + `created` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modified` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_tenant_space` (`_tenant_id`, `space_id`, `target_type`), + KEY `idx_tenant_user` (`_tenant_id`, `user_id`) +); + +CREATE TABLE IF NOT EXISTS `resource_group_relation` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `_tenant_id` BIGINT NOT NULL COMMENT '租户ID', + `target_type` VARCHAR(50) NOT NULL COMMENT '资源类型:Plugin、Workflow', + `target_id` BIGINT NOT NULL COMMENT '资源ID', + `group_id` BIGINT NOT NULL COMMENT '分组ID', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_group_id` (`group_id`), + KEY `idx_target` (`target_type`, `target_id`) +); + +CREATE TABLE IF NOT EXISTS `resource_group` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键ID', + `_tenant_id` BIGINT NOT NULL COMMENT '租户ID', + `space_id` BIGINT NOT NULL COMMENT '空间ID', + `creator_id` BIGINT NOT NULL COMMENT '创建者ID', + `name` VARCHAR(255) NOT NULL COMMENT '分组名称', + `description` VARCHAR(500) COMMENT '分组名称', + `icon` VARCHAR(2000) COMMENT '分组icon', + `tool_count` INT NOT NULL DEFAULT 0 COMMENT '工具数量', + `type` VARCHAR(50) NOT NULL COMMENT '分组类型:Plugin、Workflow', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `modified` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '修改时间', + PRIMARY KEY (`id`), + KEY `idx_space_id` (`space_id`, `type`) +); + +CREATE TABLE IF NOT EXISTS `knowledge_recall_verification` +( + `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键id', + `kb_id` BIGINT NOT NULL COMMENT '文档所属知识库', + `query_text` VARCHAR(128) NOT NULL COMMENT '查询内容', + `_tenant_id` BIGINT NOT NULL COMMENT '租户ID', + `space_id` BIGINT COMMENT '所属空间ID', + `created` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', + `creator_id` BIGINT COMMENT '创建人id', + `creator_name` VARCHAR(64) COMMENT '创建人', + `modified` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '更新时间', + `modified_id` BIGINT COMMENT '最后修改人id', + `modified_name` VARCHAR(64) COMMENT '最后修改人', + `search_strategy` VARCHAR(255), + `query_result` LongText, + PRIMARY KEY (`id`), + KEY `idx_id_kb_id_index` (`space_id`, `kb_id`), + KEY `idx_kb_id` (`kb_id`) +); + +CALL qiming_add_index_if_not_exists('agent_component_config', 'idx_type', + 'ALTER TABLE `agent_component_config` ADD KEY `idx_type` (`type`, `target_id`)'); +CALL qiming_add_index_if_not_exists('agent_component_config', 'idx_agent_id', + 'ALTER TABLE `agent_component_config` ADD KEY `idx_agent_id` (`agent_id`)'); + +CALL qiming_add_column_if_not_exists('model_config', 'sort', + 'ALTER TABLE `model_config` ADD COLUMN `sort` INT NOT NULL DEFAULT 0 COMMENT ''排序'''); + +CALL qiming_add_column_if_not_exists('published', 'group_id', + 'ALTER TABLE `published` ADD COLUMN `group_id` BIGINT COMMENT ''分组id'''); +CALL qiming_add_index_if_not_exists('published', 'idx_group_id', + 'ALTER TABLE `published` ADD KEY `idx_group_id` (`group_id`)'); +CALL qiming_add_index_if_not_exists('published', 'ft_name_desc', + 'ALTER TABLE `published` ADD FULLTEXT KEY `ft_name_desc` (`name`, `description`) WITH PARSER ngram'); + +CALL qiming_add_column_if_not_exists('agent_config', 'enable_version_control', + 'ALTER TABLE `agent_config` ADD COLUMN `enable_version_control` TINYINT NOT NULL DEFAULT 0 COMMENT ''是否启用版本控制'''); +CALL qiming_add_column_if_not_exists('agent_config', 'dev_agent_conversation_id', + 'ALTER TABLE `agent_config` ADD COLUMN `dev_agent_conversation_id` BIGINT COMMENT ''开发Agent的会话ID'''); +CALL qiming_add_column_if_not_exists('agent_config', 'allow_choose_mode', + 'ALTER TABLE `agent_config` ADD COLUMN `allow_choose_mode` TINYINT NOT NULL DEFAULT 0 COMMENT ''允许选择Agent交互模式'''); +CALL qiming_add_column_if_not_exists('agent_config', 'sub_type', + 'ALTER TABLE `agent_config` ADD COLUMN `sub_type` VARCHAR(32) COMMENT ''智能体(应用)子类型'''); +CALL qiming_add_column_if_not_exists('agent_config', 'publish_version', + 'ALTER TABLE `agent_config` ADD COLUMN `publish_version` JSON COMMENT ''发布版本记录'''); +CALL qiming_add_column_if_not_exists('agent_config', 'enable_ask_question', + 'ALTER TABLE `agent_config` ADD COLUMN `enable_ask_question` TINYINT NOT NULL DEFAULT 1 COMMENT ''打开询问用户'''); + +CALL qiming_add_column_if_not_exists('workflow_config', 'type', + 'ALTER TABLE `workflow_config` ADD COLUMN `type` VARCHAR(32) DEFAULT ''Workflow'' COMMENT ''类型:Workflow和AgentFlow'''); +CALL qiming_add_column_if_not_exists('workflow_config', 'agent_id', + 'ALTER TABLE `workflow_config` ADD COLUMN `agent_id` BIGINT COMMENT ''类型为AgentFlow时关联的AgentID'''); + +CALL qiming_add_column_if_not_exists('conversation', 'dev_target_type', + 'ALTER TABLE `conversation` ADD COLUMN `dev_target_type` VARCHAR(32) COMMENT ''开发的目标对象'''); +CALL qiming_add_column_if_not_exists('conversation', 'dev_target_id', + 'ALTER TABLE `conversation` ADD COLUMN `dev_target_id` VARCHAR(32) COMMENT ''开发的目标对象ID'''); +CALL qiming_add_column_if_not_exists('conversation', 'icon', + 'ALTER TABLE `conversation` ADD COLUMN `icon` VARCHAR(500) COMMENT ''会话主题图标'''); +CALL qiming_add_column_if_not_exists('conversation', 'dev_space_id', + 'ALTER TABLE `conversation` ADD COLUMN `dev_space_id` BIGINT COMMENT ''开发所在的空间ID'''); + +CALL qiming_modify_column_if_exists('pricing_config', 'price', + 'ALTER TABLE `pricing_config` MODIFY COLUMN `price` DECIMAL(20,6) COMMENT ''价格(单次、买断、包月时有效)'''); +CALL qiming_modify_column_if_exists('knowledge_config', 'description', + 'ALTER TABLE `knowledge_config` MODIFY COLUMN `description` VARCHAR(10000) COMMENT ''知识库描述'''); +CALL qiming_modify_column_if_exists('bill_order', 'amount', + 'ALTER TABLE `bill_order` MODIFY COLUMN `amount` DECIMAL(20,6) NOT NULL DEFAULT 0.000000 COMMENT ''订单金额'''); +CALL qiming_modify_column_if_exists('custom_table_definition', 'table_description', + 'ALTER TABLE `custom_table_definition` MODIFY COLUMN `table_description` VARCHAR(10000) COMMENT ''表描述'''); +CALL qiming_modify_column_if_exists('bill_order_item', 'price', + 'ALTER TABLE `bill_order_item` MODIFY COLUMN `price` DECIMAL(20,6) NOT NULL DEFAULT 0.000000 COMMENT ''单价'''); +CALL qiming_modify_column_if_exists('resource_group', 'icon', + 'ALTER TABLE `resource_group` MODIFY COLUMN `icon` VARCHAR(2000) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NULL COMMENT ''分组icon'''); + +CALL qiming_add_column_if_not_exists('skill_config', 'dev_agent_conversation_id', + 'ALTER TABLE `skill_config` ADD COLUMN `dev_agent_conversation_id` BIGINT COMMENT ''开发会话ID'''); + +CALL qiming_add_column_if_not_exists('plugin_config', 'dev_agent_conversation_id', + 'ALTER TABLE `plugin_config` ADD COLUMN `dev_agent_conversation_id` BIGINT COMMENT ''开发会话ID'''); + +CALL qiming_add_index_if_not_exists('workflow_node_config', 'idx_workflow_id', + 'ALTER TABLE `workflow_node_config` ADD KEY `idx_workflow_id` (`workflow_id`)'); + +DROP PROCEDURE IF EXISTS qiming_add_column_if_not_exists; +DROP PROCEDURE IF EXISTS qiming_add_index_if_not_exists; +DROP PROCEDURE IF EXISTS qiming_modify_column_if_exists;