瀏覽代碼

医生端验证码

lk 2 天之前
父節點
當前提交
12ae2f4e07

+ 2 - 1
java/fs-ai-api/src/main/resources/application.yml

@@ -10,7 +10,8 @@ spring:
     #    active: druid-sxjz-test
     #    active: druid-sft
     #    active: druid-fby
-    active: ylrz
+    active: dev
+#    active: ylrz
     include: common,config-dev
 
 swagger:

+ 6 - 6
java/fs-doctor-app/src/main/java/com/fs/app/controller/CommonController.java

@@ -259,12 +259,12 @@ public class CommonController {
 	public AjaxResult getCode(HttpServletResponse response) throws IOException
 	{
 		AjaxResult ajax = AjaxResult.success();
-		boolean captchaOnOff = configService.selectCaptchaOnOff();
-		ajax.put("captchaOnOff", captchaOnOff);
-		if (!captchaOnOff)
-		{
-			return ajax;
-		}
+		// boolean captchaOnOff = configService.selectCaptchaOnOff();
+		// ajax.put("captchaOnOff", captchaOnOff);
+		// if (!captchaOnOff)
+		// {
+		// 	return ajax;
+		// }
 
 		// 保存验证码信息
 		String uuid = IdUtils.simpleUUID();

+ 4 - 0
java/fs-service/src/main/java/com/fs/company/service/impl/CompanyWorkflowLobsterServiceImpl.java

@@ -910,10 +910,14 @@ public class CompanyWorkflowLobsterServiceImpl implements ICompanyWorkflowLobste
         if (edges != null && !edges.isEmpty()) {
             List<CompanyWorkflowLobsterEdge> edgeEntities = new ArrayList<>();
             int idx = 0;
+            int edgeSeq = 0;
             for (CompanyWorkflowLobsterEdgeParam it : edges) {
                 CompanyWorkflowLobsterEdge e = new CompanyWorkflowLobsterEdge();
                 BeanUtils.copyProperties(it, e);
                 e.setWorkflowId(workflowId);
+                if (!org.springframework.util.StringUtils.hasText(e.getEdgeKey())) {
+                    e.setEdgeKey("E" + System.currentTimeMillis() + "_" + (edgeSeq++));
+                }
                 if (e.getSortNo() == null) {
                     e.setSortNo(idx++);
                 }

文件差異過大導致無法顯示
+ 267 - 0
java/fs-service/src/main/resources/db/tenant-initData.sql


+ 1373 - 0
java/fs-service/src/main/resources/db/tenant-initTable.sql

@@ -18451,6 +18451,7 @@ CREATE TABLE `lobster_execution_config` (
     `company_id` bigint NOT NULL COMMENT '租户全局行使用0',
     `default_execution_mode` varchar(16) NOT NULL DEFAULT 'HYBRID' COMMENT 'PROMPT/INSTANCE/HYBRID',
     `auto_start_instance_on_tag` tinyint NOT NULL DEFAULT 0 COMMENT '打标签时自动 startWorkflow',
+    `lobster_enabled` tinyint NOT NULL DEFAULT 1 COMMENT '1=启动数字员工;0=关闭全部数字员工自动运行和AI自动回复',
     `quality_gate_enabled` tinyint NOT NULL DEFAULT 1 COMMENT '1=启用质量门控',
     `quality_pass_score_percent` int NOT NULL DEFAULT 75 COMMENT '单轮回复通过率(%)',
     `quality_target_score_percent` int NOT NULL DEFAULT 75 COMMENT 'E2E单节点质量目标(%)',
@@ -18461,6 +18462,19 @@ CREATE TABLE `lobster_execution_config` (
     `message_dedup_exact_window_size` int NOT NULL DEFAULT 5 COMMENT '精确去重窗口条数',
     `message_dedup_semantic_threshold` decimal(4,2) NOT NULL DEFAULT 0.85 COMMENT '语义相似阈值',
     `message_dedup_window_duration_seconds` int NOT NULL DEFAULT 300 COMMENT '去重窗口时长(秒)',
+    `passive_reply_only` tinyint NOT NULL DEFAULT 0 COMMENT '1=仅响应模式(不主动发送,只回复客户消息)',
+    `strict_kb_mode` tinyint NOT NULL DEFAULT 0 COMMENT '1=严格知识库模式(知识库范围外不回答,转人工)',
+    `reply_depth_mode` varchar(16) NOT NULL DEFAULT 'STANDARD' COMMENT '回复速度与思考深度:FAST/STANDARD/CAREFUL',
+    `llm_timeout_protection_enabled` tinyint NOT NULL DEFAULT 1 COMMENT '1=启用LLM超时保护',
+    `reply_timeout_seconds` int NOT NULL DEFAULT 5 COMMENT '默认回复超时时间(秒)',
+    `proactive_probe_enabled` tinyint NOT NULL DEFAULT 1 COMMENT '1=允许主动追问',
+    `max_consecutive_probe_count` int NOT NULL DEFAULT 1 COMMENT '最大连续追问次数',
+    `stop_probe_on_negative_signal` tinyint NOT NULL DEFAULT 1 COMMENT '1=客户拒绝/忙碌后停止追问',
+    `high_risk_sensitive_rewrite_enabled` tinyint NOT NULL DEFAULT 1 COMMENT '1=高风险敏感内容安全改写,不直接拦截',
+    `token_budget_enabled` tinyint NOT NULL DEFAULT 1 COMMENT '1=启用Token预算保护',
+    `daily_token_limit` bigint NOT NULL DEFAULT 0 COMMENT '单租户每日Token上限,0=不限',
+    `joke_allowed` tinyint NOT NULL DEFAULT 1 COMMENT '1=允许玩笑',
+    `light_flirt_allowed` tinyint NOT NULL DEFAULT 0 COMMENT '1=允许轻暧昧闲聊',
     `create_by` varchar(64) DEFAULT NULL,
     `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
     `update_by` varchar(64) DEFAULT NULL,
@@ -22390,3 +22404,1362 @@ CREATE TABLE IF NOT EXISTS `wx_publish_moments_task` (
     KEY `idx_wx_publish_moments_task_update_time` (`update_time`) USING BTREE
 ) ENGINE = InnoDB COMMENT = '个微发布朋友圈任务' ROW_FORMAT = DYNAMIC;
 
+-- ==== table: ai_generation_record (source lines 21296-21313) ====
+CREATE TABLE IF NOT EXISTS `ai_generation_record` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `company_id` bigint DEFAULT NULL COMMENT '公司ID',
+  `user_id` bigint DEFAULT NULL COMMENT '用户ID',
+  `prompt` text COMMENT '用户输入/需求描述',
+  `status` varchar(20) DEFAULT 'pending' COMMENT '状态:pending/processing/completed/failed',
+  `result_json` text COMMENT '生成结果JSON(工作流定义)',
+  `error_msg` text COMMENT '错误信息',
+  `model_used` varchar(50) DEFAULT NULL COMMENT '使用的模型',
+  `tokens_used` int DEFAULT '0' COMMENT '消耗tokens',
+  `duration_ms` int DEFAULT '0' COMMENT '耗时(毫秒)',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_company_id` (`company_id`),
+  KEY `idx_status` (`status`),
+  KEY `idx_create_time` (`create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='AI工作流生成记录';
+
+-- ==== table: ai_sip_call_biz_group (source lines 21317-21325) ====
+CREATE TABLE IF NOT EXISTS `ai_sip_call_biz_group` (
+  `group_id` bigint NOT NULL AUTO_INCREMENT,
+  `biz_group_name` varchar(100) DEFAULT NULL,
+  `sort_no` int DEFAULT '0',
+  `notes` varchar(500) DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `remote_group_id` bigint DEFAULT NULL,
+  PRIMARY KEY (`group_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: ai_sip_call_gateway (source lines 21329-21348) ====
+CREATE TABLE IF NOT EXISTS `ai_sip_call_gateway` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `gw_name` varchar(100) DEFAULT NULL,
+  `profile_name` varchar(100) DEFAULT NULL,
+  `caller` varchar(50) DEFAULT NULL,
+  `callee_prefix` varchar(20) DEFAULT NULL,
+  `gw_addr` varchar(100) DEFAULT NULL,
+  `codec` varchar(20) DEFAULT NULL,
+  `gw_desc` varchar(500) DEFAULT NULL,
+  `max_concurrency` bigint DEFAULT '10',
+  `auth_username` varchar(100) DEFAULT NULL,
+  `auth_password` varchar(100) DEFAULT NULL,
+  `priority` bigint DEFAULT '5',
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `register` bigint DEFAULT '0',
+  `configs` text,
+  `purpose` bigint DEFAULT '3',
+  `remote_gateway_id` bigint DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: ai_sip_call_llm_agent_account (source lines 21352-21367) ====
+CREATE TABLE IF NOT EXISTS `ai_sip_call_llm_agent_account` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `account_json` text,
+  `provider_class_name` varchar(200) DEFAULT NULL,
+  `name` varchar(100) DEFAULT NULL,
+  `account_entity` varchar(200) DEFAULT NULL,
+  `interrupt_flag` int DEFAULT '0',
+  `interrupt_keywords` varchar(500) DEFAULT NULL,
+  `interrupt_ignore_keywords` varchar(500) DEFAULT NULL,
+  `intention_tips` varchar(500) DEFAULT NULL,
+  `concurrent_num` int DEFAULT '1',
+  `transfer_manual_digit` varchar(50) DEFAULT NULL,
+  `kb_cat_id` bigint DEFAULT NULL,
+  `remote_llm_agent_account_id` bigint DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: ai_sip_call_phone (source lines 21391-21431) ====
+CREATE TABLE IF NOT EXISTS `ai_sip_call_phone` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `batch_id` bigint DEFAULT NULL,
+  `telephone` varchar(20) DEFAULT NULL,
+  `cust_name` varchar(50) DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `callstatus` int DEFAULT '0',
+  `callout_time` datetime DEFAULT NULL,
+  `callcount` int DEFAULT '0',
+  `call_end_time` datetime DEFAULT NULL,
+  `time_len` int DEFAULT '0',
+  `valid_time_len` int DEFAULT '0',
+  `uuid` varchar(100) DEFAULT NULL,
+  `connected_time` datetime DEFAULT NULL,
+  `hangup_cause` varchar(50) DEFAULT NULL,
+  `answered_time` datetime DEFAULT NULL,
+  `dialogue` text,
+  `wavfile` varchar(200) DEFAULT NULL,
+  `record_server_url` varchar(500) DEFAULT NULL,
+  `biz_json` text,
+  `dialogue_count` int DEFAULT '0',
+  `acd_opnum` varchar(50) DEFAULT NULL,
+  `acd_queue_time` int DEFAULT '0',
+  `acd_wait_time` int DEFAULT '0',
+  `tts_text` text,
+  `empty_number_detection_text` text,
+  `intent` varchar(50) DEFAULT NULL,
+  `asr_seconds` decimal(10,2) DEFAULT '0.00',
+  `tts_times` int DEFAULT '0',
+  `tts_flow_tokens` int DEFAULT '0',
+  `input_tokens` int DEFAULT '0',
+  `output_tokens` int DEFAULT '0',
+  `total_cost` decimal(10,4) DEFAULT '0.0000',
+  `billing_status` int DEFAULT '0',
+  `caller_number` varchar(20) DEFAULT NULL,
+  `ivr_dtmf_digits` varchar(50) DEFAULT NULL,
+  `manual_answered_time` datetime DEFAULT NULL,
+  `manual_answered_time_len` int DEFAULT '0',
+  PRIMARY KEY (`id`),
+  KEY `idx_batch_id` (`batch_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: ai_sip_call_task (source lines 21435-21451) ====
+CREATE TABLE IF NOT EXISTS `ai_sip_call_task` (
+  `batch_id` bigint NOT NULL AUTO_INCREMENT,
+  `batch_name` varchar(100) DEFAULT NULL,
+  `task_status` int DEFAULT '0',
+  `caller` varchar(50) DEFAULT NULL,
+  `gateway_id` bigint DEFAULT NULL,
+  `biz_group_id` bigint DEFAULT NULL,
+  `llm_agent_account_id` bigint DEFAULT NULL,
+  `voice_tts_aliyun_id` bigint DEFAULT NULL,
+  `total_count` int DEFAULT '0',
+  `finished_count` int DEFAULT '0',
+  `answered_count` int DEFAULT '0',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `company_id` bigint DEFAULT NULL,
+  PRIMARY KEY (`batch_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: ai_sip_call_voice_tts_aliyun (source lines 21487-21497) ====
+CREATE TABLE IF NOT EXISTS `ai_sip_call_voice_tts_aliyun` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `voice_name` varchar(100) DEFAULT NULL,
+  `voice_code` varchar(100) DEFAULT NULL,
+  `voice_enabled` int DEFAULT '1',
+  `voice_source` varchar(50) DEFAULT NULL,
+  `priority` int DEFAULT '5',
+  `provider` varchar(50) DEFAULT NULL,
+  `remote_voice_tts_aliyun_id` bigint DEFAULT NULL,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: billing_detail (source lines 21501-21521) ====
+CREATE TABLE IF NOT EXISTS `billing_detail` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `tenant_id` bigint NOT NULL,
+  `statement_id` bigint DEFAULT NULL,
+  `event_id` varchar(64) NOT NULL,
+  `event_type` varchar(32) NOT NULL,
+  `sub_type` varchar(32) DEFAULT NULL,
+  `plan_code` varchar(64) NOT NULL,
+  `plan_version` int NOT NULL,
+  `unit_price` decimal(18,6) NOT NULL,
+  `usage_value` decimal(20,6) NOT NULL,
+  `charge_value` decimal(20,6) NOT NULL COMMENT '计费用量(按最小单位折算)',
+  `amount` decimal(18,6) NOT NULL,
+  `billing_mode` varchar(16) NOT NULL COMMENT 'PREPAID/POSTPAID',
+  `occurred_at` datetime NOT NULL,
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_event_type_sub` (`event_id`,`event_type`,`sub_type`),
+  KEY `idx_tenant_time` (`tenant_id`,`occurred_at`),
+  KEY `idx_statement` (`statement_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='计费明细';
+
+-- ==== table: billing_statement (source lines 21525-21541) ====
+CREATE TABLE IF NOT EXISTS `billing_statement` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `statement_no` varchar(64) NOT NULL,
+  `tenant_id` bigint NOT NULL,
+  `period_type` varchar(16) NOT NULL COMMENT 'DAILY/MONTHLY',
+  `period_start` datetime NOT NULL,
+  `period_end` datetime NOT NULL,
+  `total_amount` decimal(18,2) NOT NULL DEFAULT '0.00',
+  `paid_amount` decimal(18,2) NOT NULL DEFAULT '0.00',
+  `unpaid_amount` decimal(18,2) NOT NULL DEFAULT '0.00',
+  `status` varchar(16) NOT NULL DEFAULT 'INIT' COMMENT 'INIT/CONFIRMED/PAID/PARTIAL',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_statement_no` (`statement_no`),
+  KEY `idx_tenant_period` (`tenant_id`,`period_start`,`period_end`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='账单';
+
+-- ==== table: billing_statement_item (source lines 21545-21556) ====
+CREATE TABLE IF NOT EXISTS `billing_statement_item` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `statement_id` bigint NOT NULL,
+  `item_code` varchar(64) NOT NULL,
+  `amount` decimal(18,2) NOT NULL DEFAULT '0.00',
+  `usage_value` decimal(20,6) NOT NULL DEFAULT '0.000000',
+  `unit` varchar(32) DEFAULT NULL,
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_stmt_item` (`statement_id`,`item_code`),
+  KEY `idx_statement_id` (`statement_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='账单明细项';
+
+-- ==== table: cc_llm_agent_account (source lines 21560-21577) ====
+CREATE TABLE IF NOT EXISTS `cc_llm_agent_account` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `name` varchar(100) DEFAULT NULL,
+  `account_json` text,
+  `provider_class_name` varchar(200) DEFAULT NULL,
+  `account_entity` varchar(200) DEFAULT NULL,
+  `interrupt_flag` tinyint DEFAULT '0',
+  `interrupt_keywords` varchar(500) DEFAULT NULL,
+  `interrupt_ignore_keywords` varchar(500) DEFAULT NULL,
+  `intention_tips` varchar(500) DEFAULT NULL,
+  `concurrent_num` int DEFAULT '1',
+  `transfer_manual_digit` varchar(50) DEFAULT NULL,
+  `kb_cat_id` bigint DEFAULT NULL,
+  `company_id` bigint DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: cc_llm_agent_provider (source lines 21581-21590) ====
+CREATE TABLE IF NOT EXISTS `cc_llm_agent_provider` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `name` varchar(100) DEFAULT NULL,
+  `class_name` varchar(200) DEFAULT NULL,
+  `config_json` text,
+  `company_id` bigint DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: cc_llm_kb_cat (source lines 21606-21614) ====
+CREATE TABLE IF NOT EXISTS `cc_llm_kb_cat` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `name` varchar(100) DEFAULT NULL,
+  `parent_id` bigint DEFAULT '0',
+  `company_id` bigint DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: company_external_api_call_log (source lines 21618-21638) ====
+CREATE TABLE IF NOT EXISTS `company_external_api_call_log` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `config_id` bigint DEFAULT NULL COMMENT '配置ID',
+  `api_code` varchar(100) DEFAULT NULL COMMENT '接口编码',
+  `http_method` varchar(20) DEFAULT NULL COMMENT '请求方式',
+  `request_url` varchar(500) DEFAULT NULL COMMENT '请求地址',
+  `request_headers_json` text COMMENT '请求头JSON',
+  `request_body` text COMMENT '请求体',
+  `response_status` int DEFAULT NULL COMMENT '响应状态码',
+  `response_body` text COMMENT '响应体',
+  `success` int DEFAULT NULL COMMENT '1成功0失败',
+  `error_message` varchar(500) DEFAULT NULL COMMENT '错误信息',
+  `duration_ms` bigint DEFAULT NULL COMMENT '耗时ms',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `create_by` varchar(64) DEFAULT NULL COMMENT '创建者',
+  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+  `update_by` varchar(64) DEFAULT NULL COMMENT '更新者',
+  PRIMARY KEY (`id`),
+  KEY `idx_config_id` (`config_id`),
+  KEY `idx_api_code` (`api_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='外部接口调用日志';
+
+-- ==== table: company_external_api_config (source lines 21642-21663) ====
+CREATE TABLE IF NOT EXISTS `company_external_api_config` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `api_type` varchar(50) DEFAULT NULL COMMENT '接口类型',
+  `api_name` varchar(200) DEFAULT NULL COMMENT '接口名称',
+  `api_code` varchar(100) DEFAULT NULL COMMENT '接口编码',
+  `api_url` varchar(500) DEFAULT NULL COMMENT '请求地址',
+  `http_method` varchar(20) DEFAULT NULL COMMENT '请求方式',
+  `rate_window_seconds` int DEFAULT NULL COMMENT '限频窗口秒数',
+  `rate_max_count` int DEFAULT NULL COMMENT '限频最大次数',
+  `priority` int DEFAULT '0' COMMENT '优先级',
+  `status` int DEFAULT '1' COMMENT '状态1启用0停用',
+  `default_headers_json` text COMMENT '默认请求头JSON',
+  `default_body_json` text COMMENT '默认请求参数JSON',
+  `timeout_ms` int DEFAULT '5000' COMMENT '超时时间ms',
+  `is_del` int DEFAULT '0' COMMENT '逻辑删除',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `create_by` varchar(64) DEFAULT NULL COMMENT '创建者',
+  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+  `update_by` varchar(64) DEFAULT NULL COMMENT '更新者',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_api_code` (`api_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='外部接口配置';
+
+-- ==== table: crm_customer_property_template (source lines 21798-21811) ====
+CREATE TABLE IF NOT EXISTS `crm_customer_property_template` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `name` varchar(100) DEFAULT NULL COMMENT '属性名称',
+  `value_type` varchar(50) DEFAULT NULL COMMENT '属性类型',
+  `ai_hint` varchar(500) DEFAULT NULL COMMENT 'AI属性提取提示',
+  `trade_type` varchar(50) DEFAULT NULL COMMENT '行业类型',
+  `intention` int DEFAULT '0' COMMENT '是否有意向度0否1是',
+  `create_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `create_by` varchar(64) DEFAULT NULL COMMENT '创建者',
+  `update_time` datetime DEFAULT NULL COMMENT '更新时间',
+  `update_by` varchar(64) DEFAULT NULL COMMENT '更新者',
+  `remark` varchar(500) DEFAULT NULL COMMENT '备注',
+  PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='CRM客户属性模板';
+
+-- ==== table: fee_plan_flow_tier (source lines 21835-21846) ====
+CREATE TABLE IF NOT EXISTS `fee_plan_flow_tier` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `plan_code` varchar(64) NOT NULL,
+  `version` int NOT NULL,
+  `min_prepay_amount` decimal(18,2) NOT NULL COMMENT '预存金额下限(元)',
+  `max_prepay_amount` decimal(18,2) DEFAULT NULL COMMENT '预存金额上限(元,空表示无上限)',
+  `unit_price` decimal(18,6) NOT NULL COMMENT '流量单价(元/GB)',
+  `sort_no` int NOT NULL DEFAULT '0',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_tier` (`plan_code`,`version`,`min_prepay_amount`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='流量阶梯定价';
+
+-- ==== table: fs_health_article (source lines 21868-21892) ====
+CREATE TABLE IF NOT EXISTS `fs_health_article` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `title` varchar(200) NOT NULL COMMENT '文章标题',
+  `summary` varchar(500) DEFAULT NULL COMMENT '摘要',
+  `content` text COMMENT '文章内容(富文本)',
+  `category` varchar(50) DEFAULT 'general' COMMENT '分类',
+  `keywords` varchar(200) DEFAULT NULL COMMENT '关键词',
+  `cover_image` varchar(500) DEFAULT NULL COMMENT '封面图',
+  `author` varchar(50) DEFAULT NULL COMMENT '作者',
+  `source` varchar(100) DEFAULT NULL COMMENT '来源',
+  `read_count` int DEFAULT '0' COMMENT '阅读量',
+  `like_count` int DEFAULT '0' COMMENT '点赞量',
+  `company_id` bigint DEFAULT NULL COMMENT '公司ID',
+  `status` tinyint DEFAULT '1' COMMENT '状态:1发布/0草稿/-1删除',
+  `is_top` tinyint DEFAULT '0' COMMENT '是否置顶',
+  `publish_time` datetime DEFAULT NULL COMMENT '发布时间',
+  `create_by` varchar(64) DEFAULT NULL,
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_by` varchar(64) DEFAULT NULL,
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_company_id` (`company_id`),
+  KEY `idx_status` (`status`),
+  KEY `idx_category` (`category`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='健康文章管理';
+
+-- ==== table: fs_pharmacist (source lines 21896-21922) ====
+CREATE TABLE IF NOT EXISTS `fs_pharmacist` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `name` varchar(50) NOT NULL COMMENT '药师姓名',
+  `title` varchar(50) DEFAULT NULL COMMENT '职称',
+  `gender` varchar(10) DEFAULT NULL COMMENT '性别',
+  `avatar` varchar(500) DEFAULT NULL COMMENT '头像',
+  `phone` varchar(20) DEFAULT NULL COMMENT '手机号',
+  `email` varchar(100) DEFAULT NULL COMMENT '邮箱',
+  `department` varchar(100) DEFAULT NULL COMMENT '科室',
+  `hospital_name` varchar(200) DEFAULT NULL COMMENT '所属医院',
+  `license_no` varchar(100) DEFAULT NULL COMMENT '执业证号',
+  `specialty` varchar(500) DEFAULT NULL COMMENT '专长领域',
+  `description` text COMMENT '简介',
+  `consultation_count` int DEFAULT '0' COMMENT '咨询量',
+  `rating` decimal(2,1) DEFAULT '5.0' COMMENT '评分',
+  `company_id` bigint DEFAULT NULL COMMENT '公司ID',
+  `status` tinyint DEFAULT '1' COMMENT '状态:1启用/0禁用',
+  `sort_order` int DEFAULT '0' COMMENT '排序',
+  `create_by` varchar(64) DEFAULT NULL,
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_by` varchar(64) DEFAULT NULL,
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_company_id` (`company_id`),
+  KEY `idx_status` (`status`),
+  KEY `idx_name` (`name`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='药师管理';
+
+-- ==== table: fs_store_log (source lines 21926-21950) ====
+CREATE TABLE IF NOT EXISTS `fs_store_log` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `store_id` bigint DEFAULT NULL COMMENT '店铺ID',
+  `store_name` varchar(200) DEFAULT NULL COMMENT '店铺名称',
+  `user_id` bigint DEFAULT NULL COMMENT '操作用户ID',
+  `user_name` varchar(50) DEFAULT NULL COMMENT '操作用户名',
+  `action_type` varchar(50) NOT NULL COMMENT '操作类型(create/update/delete/login/etc)',
+  `action_desc` varchar(500) DEFAULT NULL COMMENT '操作描述',
+  `module` varchar(50) DEFAULT NULL COMMENT '操作模块',
+  `target_type` varchar(50) DEFAULT NULL COMMENT '操作对象类型',
+  `target_id` bigint DEFAULT NULL COMMENT '操作对象ID',
+  `request_params` text COMMENT '请求参数',
+  `response_result` text COMMENT '响应结果',
+  `ip_address` varchar(50) DEFAULT NULL COMMENT 'IP地址',
+  `user_agent` varchar(500) DEFAULT NULL COMMENT 'User-Agent',
+  `company_id` bigint DEFAULT NULL COMMENT '公司ID',
+  `status` tinyint DEFAULT '1' COMMENT '状态:1成功/0失败',
+  `duration_ms` int DEFAULT '0' COMMENT '耗时(毫秒)',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '操作时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_company_id` (`company_id`),
+  KEY `idx_store_id` (`store_id`),
+  KEY `idx_action_type` (`action_type`),
+  KEY `idx_create_time` (`create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='店铺操作日志';
+
+-- ==== table: help_document (source lines 23387-23408) ====
+CREATE TABLE IF NOT EXISTS `help_document` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `source_doc_id` bigint NOT NULL COMMENT 'platform doc id',
+  `title` varchar(200) NOT NULL COMMENT 'title',
+  `doc_type` varchar(20) NOT NULL DEFAULT 'file' COMMENT 'text/image/video/file',
+  `file_url` varchar(1024) DEFAULT NULL COMMENT 'file url',
+  `file_name` varchar(255) DEFAULT NULL COMMENT 'file name',
+  `file_size` bigint DEFAULT NULL COMMENT 'file size',
+  `content` mediumtext COMMENT 'text content',
+  `module_path_mgn` varchar(255) DEFAULT NULL COMMENT 'mgn path',
+  `module_path_company` varchar(255) DEFAULT NULL COMMENT 'company path',
+  `visible_scope` varchar(20) NOT NULL DEFAULT 'ALL' COMMENT 'MGN/COMPANY/ALL',
+  `dispatch_time` datetime DEFAULT NULL COMMENT 'dispatch time',
+  `status` tinyint NOT NULL DEFAULT 1 COMMENT '1 visible 0 off',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_source_doc` (`source_doc_id`),
+  KEY `idx_visible_scope` (`visible_scope`),
+  KEY `idx_status` (`status`),
+  KEY `idx_dispatch_time` (`dispatch_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='帮助文档';
+
+-- ==== table: ipad_account_license (source lines 23412-23437) ====
+CREATE TABLE IF NOT EXISTS `ipad_account_license` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `order_id` varchar(64) DEFAULT NULL COMMENT '订单号',
+  `active_code` varchar(64) NOT NULL COMMENT '许可码',
+  `status` tinyint NOT NULL DEFAULT 1 COMMENT '1未激活 2有效 3过期',
+  `address_id` varchar(64) DEFAULT NULL COMMENT '购买区域',
+  `master_server_id` bigint NOT NULL COMMENT '主库 qw_ipad_server.id',
+  `tenant_server_id` bigint DEFAULT NULL COMMENT '租户库 qw_ipad_server.id',
+  `company_id` bigint DEFAULT NULL,
+  `company_user_id` bigint DEFAULT NULL,
+  `qw_user_id` bigint DEFAULT NULL,
+  `bind_time` datetime DEFAULT NULL,
+  `expire_time` datetime DEFAULT NULL,
+  `validity_month` int DEFAULT NULL COMMENT '购买月数',
+  `purchase_unit_price` decimal(10,4) DEFAULT NULL COMMENT '锁定价元/月/个',
+  `purchase_fee_unit` varchar(32) DEFAULT NULL,
+  `pricing_source` varchar(20) DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `remark` varchar(500) DEFAULT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_active_code` (`active_code`),
+  KEY `idx_status_expire` (`status`,`expire_time`),
+  KEY `idx_qw_user_id` (`qw_user_id`),
+  KEY `idx_master_server_id` (`master_server_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='iPad账号费许可';
+
+-- ==== table: lobster_auto_tags (source lines 23442-23455) ====
+CREATE TABLE IF NOT EXISTS `lobster_auto_tags`
+(
+    `id`          bigint       NOT NULL AUTO_INCREMENT,
+    `type`        int          NULL DEFAULT NULL COMMENT '类型 1客户打标签 3分时段打标签',
+    `rule_name`   varchar(255) NULL DEFAULT NULL COMMENT '规则名称',
+    `apply_users` varchar(2000) NULL DEFAULT NULL COMMENT '生效成员ID,逗号分隔',
+    `rules_tags`  text         NULL COMMENT '规则及标签JSON',
+    `is_apply`    varchar(4)   NULL DEFAULT '1' COMMENT '是否启用 1启用 2停用',
+    `create_name` varchar(255) NULL DEFAULT NULL COMMENT '创建人',
+    `create_time` datetime     NULL DEFAULT NULL COMMENT '创建时间',
+    `company_id`  bigint       NULL DEFAULT NULL COMMENT '公司ID',
+    PRIMARY KEY (`id`) USING BTREE,
+    INDEX `idx_company_type` (`company_id`, `type`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '龙虾自动打标签规则表' ROW_FORMAT = DYNAMIC;
+
+-- ==== table: lobster_billing_record (source lines 21987-22002) ====
+CREATE TABLE IF NOT EXISTS `lobster_billing_record` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `billing_type` varchar(50) DEFAULT NULL,
+  `token_count` int DEFAULT '0',
+  `token_coefficient` decimal(10,4) DEFAULT '1.0000',
+  `amount` decimal(10,4) DEFAULT '0.0000',
+  `workflow_instance_id` bigint DEFAULT NULL,
+  `node_code` varchar(100) DEFAULT NULL,
+  `model_name` varchar(100) DEFAULT NULL,
+  `company_id` bigint DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_company` (`company_id`),
+  KEY `idx_billing_type` (`billing_type`),
+  KEY `idx_create_time` (`create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='Token计费记录';
+
+-- ==== table: lobster_chat_aggregate (source lines 22006-22023) ====
+CREATE TABLE IF NOT EXISTS `lobster_chat_aggregate` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `session_id` varchar(100) DEFAULT NULL,
+  `external_user_id` varchar(100) DEFAULT NULL,
+  `channel_type` varchar(20) DEFAULT NULL,
+  `message_type` varchar(20) DEFAULT NULL,
+  `content` text,
+  `direction` varchar(10) DEFAULT NULL,
+  `workflow_instance_id` bigint DEFAULT NULL,
+  `node_code` varchar(100) DEFAULT NULL,
+  `company_id` bigint DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_session` (`session_id`),
+  KEY `idx_external_user` (`external_user_id`),
+  KEY `idx_company` (`company_id`),
+  KEY `idx_create_time` (`create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='聚合聊天记录';
+
+-- ==== table: lobster_conversation_change_event (source lines 23612-23633) ====
+CREATE TABLE IF NOT EXISTS `lobster_conversation_change_event` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `company_id` BIGINT NOT NULL COMMENT '企业ID',
+  `contact_id` BIGINT NULL COMMENT '客户/联系人ID',
+  `channel_type` VARCHAR(32) NULL COMMENT '渠道类型',
+  `event_type` VARCHAR(64) NOT NULL COMMENT '突发变动类型',
+  `old_state_json` JSON NULL COMMENT '原上下文/计划快照',
+  `new_state_json` JSON NULL COMMENT '新状态/计划快照',
+  `detected_intent` VARCHAR(128) NULL COMMENT '识别意图',
+  `suggested_action` VARCHAR(64) NULL COMMENT '建议动作',
+  `confidence` DECIMAL(6,4) NULL COMMENT '置信度',
+  `risk_level` VARCHAR(32) NULL COMMENT '风险等级',
+  `need_human_confirm` TINYINT DEFAULT 0 COMMENT '是否需要人工确认',
+  `handled_status` VARCHAR(32) DEFAULT 'pending' COMMENT '处理状态',
+  `handle_result` VARCHAR(512) NULL COMMENT '处理结果',
+  `message_text` TEXT NULL COMMENT '触发消息',
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_company_contact_time` (`company_id`, `contact_id`, `create_time`),
+  KEY `idx_company_event_time` (`company_id`, `event_type`, `create_time`),
+  KEY `idx_company_status` (`company_id`, `handled_status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='客户沟通突发变动事件表';
+
+-- ==== table: lobster_dynamic_node_run (source lines 23671-23688) ====
+CREATE TABLE IF NOT EXISTS `lobster_dynamic_node_run` (
+  `id` BIGINT AUTO_INCREMENT PRIMARY KEY,
+  `impl_id` BIGINT DEFAULT NULL COMMENT '关联学习产物id, NULL=首次AI执行',
+  `node_type` INT NOT NULL,
+  `company_id` BIGINT DEFAULT NULL,
+  `instance_id` BIGINT DEFAULT NULL COMMENT '工作流实例id',
+  `fingerprint` VARCHAR(128) DEFAULT NULL,
+  `duration_ms` INT DEFAULT 0,
+  `success` TINYINT DEFAULT 0,
+  `score` DOUBLE DEFAULT 0,
+  `exec_path` VARCHAR(16) DEFAULT 'AI' COMMENT 'AI=首次LLM / DSL=复用学习产物',
+  `input_context` TEXT DEFAULT NULL,
+  `output_result` TEXT DEFAULT NULL,
+  `error_msg` VARCHAR(512) DEFAULT NULL,
+  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP,
+  KEY `idx_impl` (`impl_id`),
+  KEY `idx_node_type` (`node_type`, `create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数字员工动态节点执行轨迹';
+
+-- ==== table: lobster_event_audit_log (source lines 22119-22134) ====
+CREATE TABLE IF NOT EXISTS `lobster_event_audit_log` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `workflow_instance_id` bigint DEFAULT NULL,
+  `node_code` varchar(100) DEFAULT NULL,
+  `event_type` varchar(50) DEFAULT NULL,
+  `event_data` text,
+  `audit_status` varchar(20) DEFAULT 'pending',
+  `auditor` varchar(100) DEFAULT NULL,
+  `audit_remark` text,
+  `company_id` bigint DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_workflow_instance` (`workflow_instance_id`),
+  KEY `idx_audit_status` (`audit_status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='节点审核日志';
+
+-- ==== table: lobster_handoff_follow_task (source lines 23590-23609) ====
+CREATE TABLE IF NOT EXISTS `lobster_handoff_follow_task` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `company_id` BIGINT NOT NULL COMMENT '企业ID',
+  `instance_id` BIGINT COMMENT '工作流实例ID',
+  `external_user_id` VARCHAR(100) COMMENT '客户外部ID',
+  `owner_sales_id` VARCHAR(64) DEFAULT NULL COMMENT '归属销售ID,空值表示进入企业公共接管队列',
+  `trigger_type` VARCHAR(50) COMMENT '触发类型(media/quota_exceeded/low_relevance/reply_risk/semantic_handoff/workflow_handoff等)',
+  `title` VARCHAR(200) COMMENT '待办标题',
+  `content` VARCHAR(1000) COMMENT '待办内容/提醒文案',
+  `status` TINYINT DEFAULT 0 COMMENT '状态(0待处理 1已处理 2已忽略)',
+  `handle_by` VARCHAR(64) COMMENT '处理人',
+  `handle_time` DATETIME COMMENT '处理时间',
+  `handle_remark` VARCHAR(500) COMMENT '处理备注',
+  `create_time` DATETIME COMMENT '创建时间',
+  `update_time` DATETIME COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  INDEX `idx_company_sales` (`company_id`, `owner_sales_id`),
+  INDEX `idx_company_status` (`company_id`, `status`),
+  INDEX `idx_instance` (`instance_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='人工接管跟进待办';
+
+-- ==== table: lobster_handoff_policy (source lines 23561-23587) ====
+CREATE TABLE IF NOT EXISTS `lobster_handoff_policy` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `company_id` BIGINT NOT NULL COMMENT '企业ID',
+  `enabled` TINYINT DEFAULT 1 COMMENT '是否启用自动人工接管提醒',
+  `trigger_manual_request` TINYINT DEFAULT 1 COMMENT '客户明确要求人工/负责人时提醒',
+  `trigger_high_risk` TINYINT DEFAULT 1 COMMENT '投诉/退款/举报/法律风险时提醒',
+  `trigger_urgency` TINYINT DEFAULT 1 COMMENT '客户明显着急或不满时提醒',
+  `trigger_low_relevance` TINYINT DEFAULT 1 COMMENT 'AI连续答非所问时提醒',
+  `trigger_media` TINYINT DEFAULT 1 COMMENT '图片/视频/文件等需要人工查看时提醒',
+  `trigger_reply_risk` TINYINT DEFAULT 1 COMMENT 'AI回复高风险被拦截时提醒',
+  `trigger_quota_exceeded` TINYINT DEFAULT 1 COMMENT 'AI额度用完时提醒',
+  `notify_owner_sales` TINYINT DEFAULT 1 COMMENT '固定提醒客户归属销售',
+  `pause_ai` TINYINT DEFAULT 1 COMMENT '触发后暂停AI自动回复',
+  `silent_to_customer` TINYINT DEFAULT 1 COMMENT '客户侧无感,不发送转人工提示',
+  `create_follow_task` TINYINT DEFAULT 1 COMMENT '创建销售跟进待办',
+  `record_reason` TINYINT DEFAULT 1 COMMENT '记录接管原因',
+  `customer_message` VARCHAR(500) DEFAULT NULL COMMENT '可选客户提示语,默认不发送',
+  `internal_notice` VARCHAR(500) DEFAULT '客户需要人工跟进,请及时处理' COMMENT '提醒归属销售的内部提示',
+  `status` TINYINT DEFAULT 1 COMMENT '状态 1正常 0停用',
+  `create_by` VARCHAR(64) DEFAULT NULL COMMENT '创建人',
+  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  `update_by` VARCHAR(64) DEFAULT NULL COMMENT '更新人',
+  `update_time` DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_company` (`company_id`),
+  KEY `idx_enabled` (`enabled`, `status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数字员工-人工接管提醒策略';
+
+-- ==== table: lobster_knowledge_probing_meta (source lines 19739-19757) ====
+CREATE TABLE `lobster_knowledge_probing_meta` (
+  `id`                 BIGINT       NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `company_id`         BIGINT       NOT NULL COMMENT '租户ID',
+  `knowledge_id`       VARCHAR(128) NOT NULL COMMENT '关联知识库条目ID',
+  `knowledge_title`    VARCHAR(255) DEFAULT NULL COMMENT '知识标题',
+  `dimension_key`      VARCHAR(64)  NOT NULL COMMENT '维度 key',
+  `dimension_label`    VARCHAR(64)  DEFAULT NULL COMMENT '维度中文名',
+  `required`           TINYINT(1)   DEFAULT 0 COMMENT '是否必填:1=必填,0=可选',
+  `probe_hint`         VARCHAR(512) DEFAULT NULL COMMENT '内部追问意图',
+  `dimension_type`     VARCHAR(32)  DEFAULT NULL COMMENT '维度类型:enum/number/string/range',
+  `dimension_options`  TEXT         DEFAULT NULL COMMENT '枚举选项(JSON数组)',
+  `priority`           INT          DEFAULT 100 COMMENT '优先级(越小越优先)',
+  `enabled`            TINYINT(1)   DEFAULT 1 COMMENT '是否启用',
+  `create_time`        DATETIME     DEFAULT CURRENT_TIMESTAMP,
+  `update_time`        DATETIME     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_company_knowledge` (`company_id`, `knowledge_id`, `enabled`),
+  KEY `idx_company_enabled` (`company_id`, `enabled`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='知识库追问维度元数据';
+
+-- ==== table: lobster_optimization_suggestion (source lines 22304-22320) ====
+CREATE TABLE IF NOT EXISTS `lobster_optimization_suggestion` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `workflow_id` bigint DEFAULT NULL,
+  `suggestion_type` varchar(50) DEFAULT NULL,
+  `suggestion_content` text,
+  `score` decimal(5,2) DEFAULT NULL,
+  `status` varchar(20) DEFAULT 'pending',
+  `applied_at` datetime DEFAULT NULL,
+  `company_id` bigint DEFAULT NULL,
+  `create_by` varchar(64) DEFAULT '',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_by` varchar(64) DEFAULT '',
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_workflow` (`workflow_id`),
+  KEY `idx_status` (`status`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='优化建议';
+
+-- ==== table: lobster_privacy_policy (source lines 23708-23719) ====
+CREATE TABLE IF NOT EXISTS `lobster_privacy_policy` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT,
+  `company_id` BIGINT NOT NULL,
+  `decision_retention_days` INT NOT NULL DEFAULT 180,
+  `trace_retention_days` INT NOT NULL DEFAULT 365,
+  `dead_letter_retention_days` INT NOT NULL DEFAULT 90,
+  `enabled` TINYINT NOT NULL DEFAULT 1,
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_lobster_privacy_company` (`company_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数字员工租户级隐私保留策略;未配置租户自动使用平台默认值';
+
+-- ==== table: lobster_prompt_category (source lines 19891-19904) ====
+CREATE TABLE `lobster_prompt_category` (
+  `id`            bigint       NOT NULL AUTO_INCREMENT,
+  `category_code` varchar(50)  NOT NULL COMMENT '分类编码,对应 lobster_system_prompt.prompt_category',
+  `category_name` varchar(100) NOT NULL COMMENT '分类名称',
+  `description`   varchar(500) DEFAULT NULL COMMENT '说明',
+  `sort_order`    int          DEFAULT 0,
+  `enabled`       int          DEFAULT 1 COMMENT '1启用 0禁用',
+  `create_by`     varchar(64)  DEFAULT NULL,
+  `create_time`   datetime     DEFAULT CURRENT_TIMESTAMP,
+  `update_time`   datetime     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_category_code` (`category_code`),
+  KEY `idx_enabled_sort` (`enabled`, `sort_order`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AI数字员工提示词分类';
+
+-- ==== table: lobster_prompt_var (source lines 19910-19926) ====
+CREATE TABLE `lobster_prompt_var` (
+  `id`            bigint       NOT NULL AUTO_INCREMENT,
+  `var_code`      varchar(64)  NOT NULL COMMENT '变量编码,对应模板 {{var_code}}',
+  `var_name`      varchar(100) NOT NULL COMMENT '显示名称',
+  `var_type`      varchar(20)  NOT NULL COMMENT 'literal|cache|java',
+  `var_value`     varchar(1000) DEFAULT NULL COMMENT 'literal=固定值; cache=key模板; java=Resolver.code',
+  `default_value` varchar(500)  DEFAULT NULL COMMENT '取不到时的默认值',
+  `description`   varchar(500)  DEFAULT NULL COMMENT '说明/示例',
+  `enabled`       int          DEFAULT 1 COMMENT '1启用 0禁用',
+  `sort_order`    int          DEFAULT 0,
+  `create_by`     varchar(64)  DEFAULT NULL,
+  `create_time`   datetime     DEFAULT CURRENT_TIMESTAMP,
+  `update_time`   datetime     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_var_code` (`var_code`),
+  KEY `idx_enabled_type` (`enabled`, `var_type`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='提示词变量字典({{code}}替换)';
+
+-- ==== table: lobster_reply_decision_log (source lines 23636-23668) ====
+CREATE TABLE IF NOT EXISTS `lobster_reply_decision_log` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `trace_id` VARCHAR(80) DEFAULT NULL COMMENT '链路追踪ID',
+  `company_id` BIGINT NOT NULL COMMENT '企业ID',
+  `contact_id` BIGINT DEFAULT NULL COMMENT '客户ID',
+  `channel_type` VARCHAR(32) DEFAULT NULL COMMENT '渠道',
+  `template_id` BIGINT DEFAULT NULL COMMENT '模板ID',
+  `node_code` VARCHAR(64) DEFAULT NULL COMMENT '节点编码',
+  `customer_message` TEXT COMMENT '客户消息',
+  `reply_text` TEXT COMMENT 'AI回复',
+  `strategy_json` LONGTEXT COMMENT '策略编排结果JSON',
+  `context_json` LONGTEXT COMMENT '上下文排序结果JSON',
+  `quality_json` LONGTEXT COMMENT '质量评分结果JSON',
+  `used_knowledge_summary` TEXT COMMENT '命中知识库摘要',
+  `used_sales_corpus_summary` TEXT COMMENT '命中销冠语料摘要',
+  `used_industry_corpus_summary` TEXT COMMENT '命中行业语料摘要',
+  `quality_score` INT DEFAULT NULL COMMENT '质量分',
+  `risk_level` VARCHAR(32) DEFAULT NULL COMMENT '风险等级',
+  `action` VARCHAR(64) DEFAULT NULL COMMENT '处理动作',
+  `send_status` VARCHAR(32) DEFAULT 'pending' COMMENT '发送状态',
+  `actual_sent_text` TEXT COMMENT '经过出站审核后客户实际收到的文本',
+  `delivery_error` VARCHAR(1000) DEFAULT NULL COMMENT '渠道投递失败原因',
+  `delivered_at` DATETIME DEFAULT NULL COMMENT '实际送达时间',
+  `business_outcome` VARCHAR(64) DEFAULT NULL COMMENT '客户回复/预约/成交/投诉等真实业务结果',
+  `outcome_value` VARCHAR(1000) DEFAULT NULL COMMENT '业务结果摘要或外部业务单号',
+  `outcome_at` DATETIME DEFAULT NULL COMMENT '业务结果发生时间',
+  `response_ms` BIGINT DEFAULT NULL COMMENT '响应耗时ms',
+  `create_time` DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_company_contact_time` (`company_id`, `contact_id`, `create_time`),
+  KEY `idx_trace_id` (`trace_id`),
+  KEY `idx_company_time` (`company_id`, `create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数字员工AI回复决策日志';
+
+-- ==== table: lobster_reply_trace_stage (source lines 23542-23558) ====
+CREATE TABLE IF NOT EXISTS `lobster_reply_trace_stage` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '主键',
+  `trace_id` VARCHAR(64) NOT NULL COMMENT '单次回复链路ID',
+  `company_id` BIGINT NULL COMMENT '租户/企业ID',
+  `contact_id` BIGINT NULL COMMENT '客户联系人ID',
+  `channel_type` VARCHAR(32) NULL COMMENT '渠道类型',
+  `route_type` VARCHAR(32) NULL COMMENT '路由类型: prompt/instance/outbound',
+  `stage_name` VARCHAR(64) NOT NULL COMMENT '阶段名称',
+  `duration_ms` BIGINT NOT NULL DEFAULT 0 COMMENT '阶段耗时毫秒',
+  `success` TINYINT(1) NOT NULL DEFAULT 1 COMMENT '是否成功',
+  `detail` VARCHAR(1000) NULL COMMENT '阶段详情',
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+  PRIMARY KEY (`id`),
+  KEY `idx_trace_id` (`trace_id`),
+  KEY `idx_company_contact_time` (`company_id`, `contact_id`, `create_time`),
+  KEY `idx_stage_time` (`stage_name`, `create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数字员工回复链路阶段耗时记录';
+
+-- ==== table: lobster_runtime_asset_pointer (source lines 23740-23752) ====
+CREATE TABLE IF NOT EXISTS `lobster_runtime_asset_pointer` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT,
+  `scope_company_id` BIGINT NOT NULL DEFAULT 0,
+  `asset_type` VARCHAR(32) NOT NULL,
+  `asset_key` VARCHAR(191) NOT NULL,
+  `active_version_id` BIGINT NOT NULL,
+  `pinned` TINYINT NOT NULL DEFAULT 0 COMMENT '1=回滚锁定,源数据变化不自动覆盖',
+  `update_by` VARCHAR(64) NOT NULL DEFAULT 'system',
+  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_asset_pointer` (`scope_company_id`, `asset_type`, `asset_key`),
+  KEY `idx_asset_active_version` (`active_version_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Prompt与语料当前生效版本指针';
+
+-- ==== table: lobster_runtime_asset_version (source lines 23722-23738) ====
+CREATE TABLE IF NOT EXISTS `lobster_runtime_asset_version` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT COMMENT '不可变版本ID',
+  `scope_company_id` BIGINT NOT NULL DEFAULT 0 COMMENT '0=平台级,其他=租户company_id',
+  `asset_type` VARCHAR(32) NOT NULL COMMENT 'prompt/corpus',
+  `asset_key` VARCHAR(191) NOT NULL COMMENT '稳定资产键',
+  `source_record_id` BIGINT NULL COMMENT '来源表记录ID',
+  `version_no` INT NOT NULL COMMENT '同一资产递增版本号',
+  `content_hash` VARCHAR(80) NOT NULL COMMENT 'sha256内容指纹',
+  `payload_json` LONGTEXT NOT NULL COMMENT '该版本完整不可变载荷',
+  `status` VARCHAR(20) NOT NULL DEFAULT 'published' COMMENT 'published/retired',
+  `created_by` VARCHAR(64) NOT NULL DEFAULT 'system',
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_asset_version` (`scope_company_id`, `asset_type`, `asset_key`, `version_no`),
+  UNIQUE KEY `uk_asset_hash` (`scope_company_id`, `asset_type`, `asset_key`, `content_hash`),
+  KEY `idx_asset_source` (`scope_company_id`, `asset_type`, `source_record_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='Prompt与语料不可变运行版本';
+
+-- ==== table: lobster_tool_execution_audit (source lines 23691-23705) ====
+CREATE TABLE IF NOT EXISTS `lobster_tool_execution_audit` (
+  `id` BIGINT NOT NULL AUTO_INCREMENT,
+  `company_id` BIGINT NOT NULL,
+  `api_key` VARCHAR(128) NOT NULL,
+  `idempotency_key` VARCHAR(255) NOT NULL,
+  `risk_level` VARCHAR(32) NOT NULL,
+  `status` VARCHAR(32) NOT NULL,
+  `attempt_count` INT NOT NULL DEFAULT 1,
+  `result_fingerprint` VARCHAR(80) NULL,
+  `create_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_lobster_tool_idempotency` (`company_id`, `idempotency_key`),
+  KEY `idx_lobster_tool_company_status` (`company_id`, `status`, `create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数字员工业务工具执行审计与幂等';
+
+-- ==== table: lobster_user_node_optimization (source lines 22429-22451) ====
+CREATE TABLE IF NOT EXISTS `lobster_user_node_optimization` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `company_id` bigint NOT NULL,
+  `workflow_id` bigint DEFAULT NULL,
+  `external_user_id` varchar(64) NOT NULL,
+  `node_code` varchar(64) NOT NULL,
+  `original_content` text,
+  `optimized_content` text,
+  `optimization_type` varchar(50) DEFAULT NULL,
+  `confidence` double DEFAULT NULL,
+  `optimization_reason` varchar(500) DEFAULT NULL,
+  `optimization_detail` text,
+  `status` varchar(20) DEFAULT 'pending',
+  `auditor_id` varchar(64) DEFAULT NULL,
+  `audit_remark` varchar(500) DEFAULT NULL,
+  `audit_time` datetime DEFAULT NULL,
+  `apply_time` datetime DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_company_user` (`company_id`,`external_user_id`),
+  KEY `idx_company_status` (`company_id`,`status`),
+  KEY `idx_company_user_node` (`company_id`,`external_user_id`,`node_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: lobster_vector_store (source lines 23317-23330) ====
+CREATE TABLE IF NOT EXISTS `lobster_vector_store` (
+    `id` bigint NOT NULL AUTO_INCREMENT,
+    `company_id` bigint NOT NULL,
+    `category` varchar(64) NOT NULL,
+    `vec_key` varchar(128) NOT NULL,
+    `text_content` text,
+    `vector` mediumtext COMMENT 'embedding JSON float[]',
+    `metadata` text COMMENT 'JSON metadata',
+    `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+    `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_company_category_key` (`company_id`, `category`, `vec_key`),
+    KEY `idx_company_category` (`company_id`, `category`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='龙虾向量存储';
+
+-- ==== table: lobster_workflow_node (source lines 22505-22515) ====
+CREATE TABLE IF NOT EXISTS `lobster_workflow_node` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `instance_id` bigint NOT NULL,
+  `node_code` varchar(100) NOT NULL,
+  `node_config` text,
+  `deleted` int DEFAULT '0',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_instance` (`instance_id`),
+  KEY `idx_node_code` (`node_code`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: proxy_balance (source lines 22519-22531) ====
+CREATE TABLE IF NOT EXISTS `proxy_balance` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `proxy_id` bigint NOT NULL COMMENT '代理ID',
+  `balance` decimal(18,4) DEFAULT '0.0000' COMMENT '余额',
+  `frozen_amount` decimal(18,4) DEFAULT '0.0000' COMMENT '冻结金额',
+  `total_recharge` decimal(18,4) DEFAULT '0.0000' COMMENT '累计充值',
+  `total_spend` decimal(18,4) DEFAULT '0.0000' COMMENT '累计消费',
+  `total_commission` decimal(18,4) DEFAULT '0.0000' COMMENT '累计佣金',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_proxy_id` (`proxy_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='代理余额';
+
+-- ==== table: proxy_info (source lines 22535-22548) ====
+CREATE TABLE IF NOT EXISTS `proxy_info` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `proxy_id` bigint NOT NULL COMMENT '代理ID',
+  `tenant_count` int DEFAULT '0' COMMENT '发展客户数',
+  `active_tenant_count` int DEFAULT '0' COMMENT '活跃客户数',
+  `total_revenue` decimal(18,4) DEFAULT '0.0000' COMMENT '总营收',
+  `this_month_revenue` decimal(18,4) DEFAULT '0.0000' COMMENT '本月营收',
+  `total_commission` decimal(18,4) DEFAULT '0.0000' COMMENT '累计佣金',
+  `last_active_time` datetime DEFAULT NULL COMMENT '最后活跃时间',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `idx_proxy_id` (`proxy_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='代理扩展信息';
+
+-- ==== table: proxy_module_usage (source lines 22552-22570) ====
+CREATE TABLE IF NOT EXISTS `proxy_module_usage` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `proxy_id` bigint NOT NULL COMMENT '代理ID',
+  `tenant_id` bigint NOT NULL COMMENT '租户ID',
+  `module_code` varchar(50) NOT NULL COMMENT '模块编码',
+  `module_name` varchar(100) NOT NULL COMMENT '模块名称',
+  `usage_date` date NOT NULL COMMENT '统计日期',
+  `call_count` int DEFAULT '0' COMMENT '调用次数',
+  `amount` decimal(10,2) DEFAULT '0.00' COMMENT '消费金额',
+  `platform_cost` decimal(10,2) DEFAULT '0.00' COMMENT '平台成本',
+  `profit` decimal(10,2) DEFAULT '0.00' COMMENT '利润',
+  `month` varchar(7) DEFAULT NULL COMMENT '统计月份',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_proxy_date` (`proxy_id`,`usage_date`),
+  KEY `idx_tenant_date` (`tenant_id`,`usage_date`),
+  KEY `idx_module` (`module_code`),
+  KEY `idx_month` (`month`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='代理模块使用统计';
+
+-- ==== table: qw_external_ai_analyze_session (source lines 22629-22636) ====
+CREATE TABLE IF NOT EXISTS `qw_external_ai_analyze_session` (
+  `session_id` bigint NOT NULL AUTO_INCREMENT,
+  `external_user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
+  `corp_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '企业id',
+  `qw_user_id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '属于用户id',
+  PRIMARY KEY (`session_id`),
+  UNIQUE KEY `idx_session` (`external_user_id`,`corp_id`,`qw_user_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;
+
+-- ==== table: tenant_balance (source lines 22640-22663) ====
+CREATE TABLE IF NOT EXISTS `tenant_balance` (
+  `balance_id` bigint NOT NULL AUTO_INCREMENT,
+  `tenant_id` bigint NOT NULL,
+  `tenant_name` varchar(100) DEFAULT NULL,
+  `total_balance` decimal(12,2) DEFAULT '0.00',
+  `ai_model_balance` decimal(10,2) DEFAULT '0.00',
+  `course_flow_balance` decimal(10,4) DEFAULT '0.0000',
+  `live_flow_balance` decimal(10,4) DEFAULT '0.0000',
+  `token_balance` decimal(10,2) DEFAULT '0.00',
+  `sms_balance` decimal(10,2) DEFAULT '0.00',
+  `manual_call_balance` decimal(10,2) DEFAULT '0.00',
+  `ai_call_balance` decimal(10,2) DEFAULT '0.00',
+  `wechat_helper_balance` decimal(10,2) DEFAULT '0.00',
+  `frozen_amount` decimal(12,2) DEFAULT '0.00',
+  `total_recharge` decimal(12,2) DEFAULT '0.00',
+  `total_consume` decimal(12,2) DEFAULT '0.00',
+  `account_fee_expire_time` date DEFAULT NULL,
+  `status` tinyint DEFAULT '1',
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `token_coefficient` decimal(4,2) DEFAULT '1.00',
+  PRIMARY KEY (`balance_id`),
+  UNIQUE KEY `uk_tenant_id` (`tenant_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: tenant_consume_record (source lines 22667-22695) ====
+CREATE TABLE IF NOT EXISTS `tenant_consume_record` (
+  `record_id` bigint NOT NULL AUTO_INCREMENT,
+  `tenant_id` bigint NOT NULL,
+  `tenant_name` varchar(100) DEFAULT NULL,
+  `proxy_id` bigint DEFAULT NULL COMMENT '代理ID',
+  `consume_type` tinyint NOT NULL,
+  `consume_type_name` varchar(50) DEFAULT NULL,
+  `amount` decimal(10,2) NOT NULL,
+  `platform_cost` decimal(10,2) DEFAULT '0.00' COMMENT '平台成本',
+  `tenant_price` decimal(10,2) DEFAULT '0.00' COMMENT '租户价格',
+  `proxy_ratio` decimal(5,2) DEFAULT '0.00' COMMENT '代理分成比例',
+  `proxy_profit` decimal(10,2) DEFAULT '0.00' COMMENT '代理利润',
+  `unit_price` decimal(10,4) DEFAULT NULL,
+  `quantity` int DEFAULT NULL,
+  `before_balance` decimal(10,2) DEFAULT NULL,
+  `after_balance` decimal(10,2) DEFAULT NULL,
+  `description` varchar(500) DEFAULT NULL COMMENT '描述',
+  `month` varchar(7) DEFAULT NULL COMMENT '月份',
+  `remark` varchar(500) DEFAULT NULL,
+  `order_no` varchar(50) DEFAULT NULL,
+  `status` tinyint DEFAULT '1',
+  `fail_reason` varchar(500) DEFAULT NULL,
+  `consume_time` datetime DEFAULT NULL,
+  `create_time` datetime DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`record_id`),
+  KEY `idx_tenant_id` (`tenant_id`),
+  KEY `idx_consume_type` (`consume_type`),
+  KEY `idx_consume_time` (`consume_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
+
+-- ==== table: tenant_wallet (source lines 22699-22711) ====
+CREATE TABLE IF NOT EXISTS `tenant_wallet` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `tenant_id` bigint NOT NULL,
+  `balance_amount` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '可用余额',
+  `frozen_amount` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '冻结金额',
+  `credit_limit` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '信用额度(可透支)',
+  `total_recharge` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '累计充值',
+  `total_cost` decimal(18,2) NOT NULL DEFAULT '0.00' COMMENT '累计消费',
+  `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_tenant_wallet` (`tenant_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='租户钱包';
+
+-- ==== table: tenant_wallet_txn (source lines 22715-22730) ====
+CREATE TABLE IF NOT EXISTS `tenant_wallet_txn` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `tenant_id` bigint NOT NULL,
+  `txn_no` varchar(64) NOT NULL,
+  `txn_type` varchar(32) NOT NULL COMMENT 'RECHARGE/CONSUME/ADJUST/REFUND',
+  `amount` decimal(18,2) NOT NULL COMMENT '交易金额(消费为负)',
+  `balance_after` decimal(18,2) NOT NULL,
+  `biz_type` varchar(64) DEFAULT NULL COMMENT 'FLOW/CALL/TOKEN/ADD_WECHAT/OPEN_ACCOUNT',
+  `biz_id` varchar(64) DEFAULT NULL COMMENT '业务ID',
+  `remark` varchar(500) DEFAULT NULL,
+  `create_by` varchar(64) DEFAULT NULL,
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_txn_no` (`txn_no`),
+  KEY `idx_tenant_time` (`tenant_id`,`create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='租户钱包流水';
+
+-- ==== table: usage_event (source lines 22734-22749) ====
+CREATE TABLE IF NOT EXISTS `usage_event` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `event_id` varchar(64) NOT NULL COMMENT '幂等事件ID',
+  `tenant_id` bigint NOT NULL,
+  `event_type` varchar(32) NOT NULL COMMENT 'FLOW/CALL/TOKEN_SOP/TOKEN_AI_REPLY/ADD_WECHAT/OPEN_ACCOUNT',
+  `sub_type` varchar(32) DEFAULT NULL COMMENT 'CALL_IN/CALL_OUT/AI_CALL等',
+  `biz_id` varchar(64) DEFAULT NULL,
+  `usage_value` decimal(20,6) NOT NULL,
+  `usage_unit` varchar(32) NOT NULL COMMENT 'GB/MB/SECOND/TOKEN/COUNT',
+  `occurred_at` datetime NOT NULL,
+  `ext_json` json DEFAULT NULL,
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `uk_event_id` (`event_id`),
+  KEY `idx_tenant_type_time` (`tenant_id`,`event_type`,`occurred_at`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='用量事件';
+
+-- ==== table: workflow_instance (source lines 22753-22776) ====
+CREATE TABLE IF NOT EXISTS `workflow_instance` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `template_id` bigint DEFAULT NULL COMMENT '模板ID',
+  `template_name` varchar(200) DEFAULT NULL COMMENT '模板名称',
+  `company_id` bigint DEFAULT NULL COMMENT '公司ID',
+  `workflow_id` varchar(100) DEFAULT NULL COMMENT '工作流定义ID',
+  `workflow_version` varchar(20) DEFAULT NULL COMMENT '工作流版本',
+  `current_node_key` varchar(100) DEFAULT NULL COMMENT '当前节点key',
+  `current_node_name` varchar(200) DEFAULT NULL COMMENT '当前节点名称',
+  `current_node_type` varchar(50) DEFAULT NULL COMMENT '当前节点类型',
+  `status` varchar(30) DEFAULT 'running' COMMENT '状态:running/completed/failed/paused/cancelled',
+  `variables` text COMMENT '运行时变量JSON',
+  `business_key` varchar(200) DEFAULT NULL COMMENT '业务关联key',
+  `start_time` datetime DEFAULT NULL COMMENT '开始时间',
+  `end_time` datetime DEFAULT NULL COMMENT '结束时间',
+  `duration_ms` bigint DEFAULT '0' COMMENT '运行时长(毫秒)',
+  `error_message` text COMMENT '错误信息',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_company_id` (`company_id`),
+  KEY `idx_status` (`status`),
+  KEY `idx_create_time` (`create_time`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='工作流实例监控(Bridge视图)';
+
+-- ==== table: workflow_template (source lines 22780-22800) ====
+CREATE TABLE IF NOT EXISTS `workflow_template` (
+  `id` bigint NOT NULL AUTO_INCREMENT,
+  `template_name` varchar(200) NOT NULL COMMENT '模板名称',
+  `description` varchar(500) DEFAULT NULL COMMENT '描述',
+  `category` varchar(50) DEFAULT 'general' COMMENT '分类',
+  `tags` varchar(200) DEFAULT NULL COMMENT '标签(逗号分隔)',
+  `industry_type` varchar(50) DEFAULT NULL COMMENT '行业类型',
+  `canvas_data` longtext COMMENT '画布数据JSON',
+  `thumbnail` varchar(500) DEFAULT NULL COMMENT '缩略图',
+  `version` varchar(20) DEFAULT '1.0' COMMENT '版本',
+  `is_public` tinyint DEFAULT '0' COMMENT '是否公开模板',
+  `company_id` bigint DEFAULT NULL COMMENT '公司ID(null=系统模板)',
+  `used_count` int DEFAULT '0' COMMENT '使用次数',
+  `status` tinyint DEFAULT '1' COMMENT '状态:1启用/0禁用',
+  `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
+  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
+  PRIMARY KEY (`id`),
+  KEY `idx_company_id` (`company_id`),
+  KEY `idx_category` (`category`),
+  KEY `idx_is_public` (`is_public`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='工作流模板库';
+
+-- ==== table: wx_lobster_tag_user_rel (source lines 17959-17977) ====
+CREATE TABLE `wx_lobster_tag_user_rel`
+(
+    `id`                   bigint       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `company_id`           bigint       NOT NULL COMMENT '公司ID(租户隔离)',
+    `contact_id`           bigint       NOT NULL COMMENT '个微联系人ID(wx_contact.id)',
+    `tag_code`             varchar(128) DEFAULT NULL COMMENT '标签编码',
+    `tag_name`             varchar(200) DEFAULT NULL COMMENT '标签名称',
+    `template_id`          bigint       DEFAULT NULL COMMENT '工作流模板ID',
+    `binding_id`           bigint       DEFAULT NULL COMMENT '标签模板绑定ID',
+    `company_user_id`      bigint       DEFAULT NULL COMMENT '销售ID',
+    `account_id`           bigint       DEFAULT NULL COMMENT '个微账号ID',
+    `channel_context_json` text         DEFAULT NULL COMMENT '渠道上下文JSON',
+    `del_flag`             tinyint      DEFAULT '0' COMMENT '0正常1删除',
+    `create_time`          datetime     DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time`          datetime     DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY (`id`) USING BTREE,
+    KEY `idx_company_contact` (`company_id`, `contact_id`) USING BTREE,
+    KEY `idx_tag_code` (`tag_code`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT = 1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci ROW_FORMAT=DYNAMIC COMMENT='个微龙虾标签客户关联表';
+
+-- ==== table: wx_moment (source lines 17042-17068) ====
+CREATE TABLE `wx_moment`
+(
+    `id`                  bigint       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `account_id`          bigint       NULL DEFAULT NULL COMMENT '个微账号ID',
+    `company_id`          bigint       NULL DEFAULT NULL COMMENT '销售公司ID',
+    `company_user_id`     bigint       NULL DEFAULT NULL COMMENT '销售人员ID',
+    `wx_contact_id`       bigint       NULL DEFAULT NULL COMMENT '关联联系人ID',
+    `client_moment_id`    varchar(300) NULL DEFAULT NULL COMMENT '客户端朋友圈唯一ID',
+    `author_wx_id`        varchar(300) NULL DEFAULT NULL COMMENT '发布者微信ID',
+    `author_nick_name`    varchar(300) NULL DEFAULT NULL COMMENT '发布者昵称',
+    `author_head_img_url` varchar(500) NULL DEFAULT NULL COMMENT '发布者头像',
+    `content`             longtext     NULL COMMENT '正文',
+    `media_type`          int          NULL DEFAULT NULL COMMENT '媒体类型1文2图3视频4链接',
+    `media_json`          longtext     NULL COMMENT '媒体列表JSON',
+    `publish_time`        datetime     NULL DEFAULT NULL COMMENT '发布时间',
+    `like_count`          int          NULL DEFAULT 0 COMMENT '点赞数',
+    `comment_count`       int          NULL DEFAULT 0 COMMENT '评论数',
+    `sync_time`           datetime     NULL DEFAULT NULL COMMENT '最近同步时间',
+    `create_time`         datetime     NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `create_by`           varchar(300) NULL DEFAULT NULL COMMENT '创建人',
+    `update_time`         datetime     NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
+    `update_by`           varchar(300) NULL DEFAULT NULL COMMENT '修改人',
+    `remark`              varchar(300) NULL DEFAULT NULL COMMENT '备注',
+    PRIMARY KEY (`id`) USING BTREE,
+    UNIQUE KEY `uk_account_client_moment` (`account_id`, `client_moment_id`) USING BTREE,
+    KEY `idx_account_publish` (`account_id`, `publish_time`) USING BTREE
+) ENGINE = InnoDB COMMENT = '个微朋友圈' ROW_FORMAT = DYNAMIC;
+
+-- ==== table: wx_moment_comment (source lines 17074-17096) ====
+CREATE TABLE `wx_moment_comment`
+(
+    `id`                       bigint       NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `moment_id`                bigint       NOT NULL COMMENT '朋友圈ID',
+    `account_id`               bigint       NULL DEFAULT NULL COMMENT '个微账号ID',
+    `wx_contact_id`            bigint       NULL DEFAULT NULL COMMENT '关联联系人ID',
+    `client_comment_id`        varchar(300) NULL DEFAULT NULL COMMENT '客户端评论/点赞ID',
+    `comment_type`             int          NULL DEFAULT 1 COMMENT '1评论2点赞',
+    `commenter_wx_id`          varchar(300) NULL DEFAULT NULL COMMENT '互动者微信ID',
+    `commenter_nick_name`      varchar(300) NULL DEFAULT NULL COMMENT '互动者昵称',
+    `content`                  longtext     NULL COMMENT '评论内容',
+    `media_json`               longtext     NULL COMMENT '评论媒体JSON',
+    `parent_client_comment_id` varchar(300) NULL DEFAULT NULL COMMENT '父评论客户端ID',
+    `comment_time`             datetime     NULL DEFAULT NULL COMMENT '评论/点赞时间',
+    `create_time`              datetime     NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `create_by`                varchar(300) NULL DEFAULT NULL COMMENT '创建人',
+    `update_time`              datetime     NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
+    `update_by`                varchar(300) NULL DEFAULT NULL COMMENT '修改人',
+    `remark`                   varchar(300) NULL DEFAULT NULL COMMENT '备注',
+    PRIMARY KEY (`id`) USING BTREE,
+    UNIQUE KEY `uk_moment_client_comment` (`moment_id`, `client_comment_id`) USING BTREE,
+    KEY `idx_moment_type` (`moment_id`, `comment_type`) USING BTREE
+) ENGINE = InnoDB COMMENT = '个微朋友圈评论与点赞' ROW_FORMAT = DYNAMIC;
+
+-- ==== table: wx_shop_config (source lines 23333-23346) ====
+CREATE TABLE IF NOT EXISTS `wx_shop_config` (
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `shop_name` varchar(128) NOT NULL COMMENT '店铺名称',
+    `app_id` varchar(64) NOT NULL COMMENT '微信小店AppId',
+    `app_secret` varchar(256) DEFAULT NULL COMMENT 'AppSecret(获取access_token)',
+    `token` varchar(128) DEFAULT NULL COMMENT 'AppKey',
+    `encoding_aes_key` varchar(64) DEFAULT NULL COMMENT '消息推送EncodingAESKey(43位)',
+    `ai_status` int DEFAULT 1 COMMENT 'AI自动回复 0开启 1关闭',
+    `company_id` bigint DEFAULT NULL COMMENT '所属公司(可选,空则取租户第一家正常公司)',
+    `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_app_id` (`app_id`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='微信小店基本参数';
+
+-- ==== table: wx_shop_contact (source lines 23348-23360) ====
+CREATE TABLE IF NOT EXISTS `wx_shop_contact` (
+    `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+    `company_id` bigint DEFAULT NULL COMMENT '解析到的公司ID',
+    `app_id` varchar(64) NOT NULL COMMENT '微信小店AppId',
+    `open_id` varchar(128) NOT NULL COMMENT '用户OpenID',
+    `union_id` varchar(128) DEFAULT NULL COMMENT '用户unionId',
+    `nick_name` varchar(128) DEFAULT NULL COMMENT '昵称',
+    `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
+    `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_app_open` (`app_id`, `open_id`),
+    KEY `idx_company_id` (`company_id`)
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='微信小店客户触点';
+
+-- ==== table: wx_shop_msg_log (source lines 23364-23384) ====
+CREATE TABLE IF NOT EXISTS `wx_shop_msg_log` (
+     `id` bigint NOT NULL AUTO_INCREMENT COMMENT '主键',
+     `app_id` varchar(64) NOT NULL,
+    `msg_id` varchar(64) NOT NULL,
+    `to_user_name` varchar(128) DEFAULT NULL COMMENT '小店UserName',
+    `from_user_name` varchar(128) DEFAULT NULL COMMENT '用户OpenID',
+    `union_id` varchar(128) DEFAULT NULL COMMENT '用户unionId',
+    `event` varchar(64) DEFAULT NULL COMMENT '事件类型',
+    `msg_event` varchar(64) DEFAULT NULL COMMENT '客服行为类型',
+    `msg_type` varchar(32) DEFAULT NULL COMMENT '消息内容类型 text/image等',
+    `direction` tinyint NOT NULL DEFAULT '1' COMMENT '1用户发来 2客服发出',
+    `content` text COMMENT '可读消息内容摘要',
+    `media_url` varchar(1024) DEFAULT NULL COMMENT '图片/视频/文件等资源地址',
+    `raw_json` mediumtext COMMENT '解密后原始JSON',
+    `create_time_wx` bigint DEFAULT NULL COMMENT '微信CreateTime',
+    `create_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '入库时间',
+    PRIMARY KEY (`id`),
+    UNIQUE KEY `uk_app_msg` (`app_id`,`msg_id`),
+    KEY `idx_from_user` (`from_user_name`),
+    KEY `idx_create_time` (`create_time`)
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci COMMENT='微信小店客服消息记录';
+
+
+-- ============================================================
+-- missing columns diff (from ylrz_saas init/tenant-initTable.sql)
+-- idempotent ALTER TABLE ADD COLUMN (skip if first column exists)
+-- ============================================================
+
+-- table: company_ai_sensitive_word :: missing [category, level, match_type, handle_type, replace_text, scene, hit_count]
+SET @_tbl = 'company_ai_sensitive_word';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'category') = 0,
+    'ALTER TABLE `company_ai_sensitive_word` ADD COLUMN `category`     varchar(50) NULL DEFAULT NULL COMMENT ''分类'', ADD COLUMN `level`        tinyint NULL DEFAULT 1 COMMENT ''敏感等级 1低2中3高'', ADD COLUMN `match_type`   tinyint NULL DEFAULT 1 COMMENT ''匹配方式 1精确2模糊3正则'', ADD COLUMN `handle_type`  tinyint NULL DEFAULT 1 COMMENT ''处理方式 1禁止2替换3警告'', ADD COLUMN `replace_text` varchar(255) NULL DEFAULT NULL COMMENT ''替换文本'', ADD COLUMN `scene`        varchar(255) NULL DEFAULT NULL COMMENT ''适用场景'', ADD COLUMN `hit_count`    int NULL DEFAULT 0 COMMENT ''命中次数''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: company_lobster_profile_config :: missing [owner_scope, stage_adjustments, dynamic_threshold_enabled]
+SET @_tbl = 'company_lobster_profile_config';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'owner_scope') = 0,
+    'ALTER TABLE `company_lobster_profile_config` ADD COLUMN `owner_scope` VARCHAR(20) NOT NULL DEFAULT ''company'' COMMENT ''归属 tenant=租户管理端 company=分公司'', ADD COLUMN `stage_adjustments` JSON DEFAULT NULL COMMENT ''按对话阶段的调整配置JSON'', ADD COLUMN `dynamic_threshold_enabled` TINYINT DEFAULT 0 COMMENT ''1=启用动态阈值''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: company_sms_temp :: missing [audit_by, audit_time, audit_remark]
+SET @_tbl = 'company_sms_temp';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'audit_by') = 0,
+    'ALTER TABLE `company_sms_temp` ADD COLUMN `audit_by`    varchar(64) NULL DEFAULT NULL COMMENT ''最近审核人'', ADD COLUMN `audit_time`  datetime NULL DEFAULT NULL COMMENT ''最近审核时间'', ADD COLUMN `audit_remark` varchar(512) NULL DEFAULT NULL COMMENT ''最近审核备注/拒绝理由''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: company_user_license :: missing [purchase_unit_price, purchase_fee_unit, pricing_source, purchase_years]
+SET @_tbl = 'company_user_license';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'purchase_unit_price') = 0,
+    'ALTER TABLE `company_user_license` ADD COLUMN `purchase_unit_price` decimal(10,4) DEFAULT NULL COMMENT ''购买时锁定单价(元/年/个)'', ADD COLUMN `purchase_fee_unit` varchar(20) DEFAULT NULL COMMENT ''购买时收费单位'', ADD COLUMN `pricing_source` varchar(20) DEFAULT NULL COMMENT ''定价来源 TENANT/GLOBAL'', ADD COLUMN `purchase_years` int DEFAULT NULL COMMENT ''购买时年数''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: company_voice_robotic_call_log_sendmsg :: missing [callback_uuid]
+SET @_tbl = 'company_voice_robotic_call_log_sendmsg';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'callback_uuid') = 0,
+    'ALTER TABLE `company_voice_robotic_call_log_sendmsg` ADD COLUMN `callback_uuid`   varchar(64) NULL DEFAULT NULL COMMENT ''短信回调uuid''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: crm_customer :: missing [trade_type]
+SET @_tbl = 'crm_customer';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'trade_type') = 0,
+    'ALTER TABLE `crm_customer` ADD COLUMN `trade_type` varchar(64) NULL DEFAULT NULL COMMENT ''客户主行业编码(对齐 LobsterIndustryCatalog),如 pharmaceutical/liquor/travel''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: customer_profile_identity :: missing [canonical_customer_id, identity_source, identity_confidence]
+SET @_tbl = 'customer_profile_identity';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'canonical_customer_id') = 0,
+    'ALTER TABLE `customer_profile_identity` ADD COLUMN `canonical_customer_id` VARCHAR(128) NULL COMMENT ''租户内统一客户ID'', ADD COLUMN `identity_source` VARCHAR(32) NOT NULL DEFAULT ''channel'' COMMENT ''crm/verified_phone/union_id/channel'', ADD COLUMN `identity_confidence` DECIMAL(5,4) NOT NULL DEFAULT 0.6000 COMMENT ''身份关联置信度''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_ab_tests :: missing [update_time]
+SET @_tbl = 'lobster_ab_tests';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'update_time') = 0,
+    'ALTER TABLE `lobster_ab_tests` ADD COLUMN `update_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_api_registry :: missing [provider, base_url, auth_type, auth_config, timeout, priority, is_backup, description]
+SET @_tbl = 'lobster_api_registry';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'provider') = 0,
+    'ALTER TABLE `lobster_api_registry` ADD COLUMN `provider` varchar(64) DEFAULT NULL COMMENT ''provider code'', ADD COLUMN `base_url` varchar(500) DEFAULT NULL COMMENT ''base URL'', ADD COLUMN `auth_type` varchar(32) DEFAULT ''none'' COMMENT ''auth type'', ADD COLUMN `auth_config` text COMMENT ''auth config'', ADD COLUMN `timeout` int DEFAULT 5000 COMMENT ''timeout ms'', ADD COLUMN `priority` int DEFAULT 0 COMMENT ''priority'', ADD COLUMN `is_backup` tinyint DEFAULT 0 COMMENT ''backup flag'', ADD COLUMN `description` text COMMENT ''description''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_execution_config :: missing [lobster_enabled, passive_reply_only, strict_kb_mode, reply_depth_mode, llm_timeout_protection_enabled, reply_timeout_seconds, proactive_probe_enabled, max_consecutive_probe_count, stop_probe_on_negative_signal, high_risk_sensitive_rewrite_enabled, token_budget_enabled, daily_token_limit, joke_allowed, light_flirt_allowed]
+SET @_tbl = 'lobster_execution_config';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'lobster_enabled') = 0,
+    'ALTER TABLE `lobster_execution_config` ADD COLUMN `lobster_enabled` tinyint NOT NULL DEFAULT 1 COMMENT ''1=启动数字员工;0=关闭全部数字员工自动运行和AI自动回复'', ADD COLUMN `passive_reply_only` tinyint NOT NULL DEFAULT 0 COMMENT ''1=仅响应模式(不主动发送,只回复客户消息)'', ADD COLUMN `strict_kb_mode` tinyint NOT NULL DEFAULT 0 COMMENT ''1=严格知识库模式(知识库范围外不回答,转人工)'', ADD COLUMN `reply_depth_mode` varchar(16) NOT NULL DEFAULT ''STANDARD'' COMMENT ''回复速度与思考深度:FAST/STANDARD/CAREFUL'', ADD COLUMN `llm_timeout_protection_enabled` tinyint NOT NULL DEFAULT 1 COMMENT ''1=启用LLM超时保护'', ADD COLUMN `reply_timeout_seconds` int NOT NULL DEFAULT 5 COMMENT ''默认回复超时时间(秒)'', ADD COLUMN `proactive_probe_enabled` tinyint NOT NULL DEFAULT 1 COMMENT ''1=允许主动追问'', ADD COLUMN `max_consecutive_probe_count` int NOT NULL DEFAULT 1 COMMENT ''最大连续追问次数'', ADD COLUMN `stop_probe_on_negative_signal` tinyint NOT NULL DEFAULT 1 COMMENT ''1=客户拒绝/忙碌后停止追问'', ADD COLUMN `high_risk_sensitive_rewrite_enabled` tinyint NOT NULL DEFAULT 1 COMMENT ''1=高风险敏感内容安全改写,不直接拦截'', ADD COLUMN `token_budget_enabled` tinyint NOT NULL DEFAULT 1 COMMENT ''1=启用Token预算保护'', ADD COLUMN `daily_token_limit` bigint NOT NULL DEFAULT 0 COMMENT ''单租户每日Token上限,0=不限'', ADD COLUMN `joke_allowed` tinyint NOT NULL DEFAULT 1 COMMENT ''1=允许玩笑'', ADD COLUMN `light_flirt_allowed` tinyint NOT NULL DEFAULT 0 COMMENT ''1=允许轻暧昧闲聊''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_handoff_events :: missing [reason, urgency, notify_owner_sales, pause_ai, silent_to_customer, create_follow_task, notify_status, owner_sales_id, follow_task_id]
+SET @_tbl = 'lobster_handoff_events';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'reason') = 0,
+    'ALTER TABLE `lobster_handoff_events` ADD COLUMN reason VARCHAR(500) DEFAULT NULL COMMENT ''接管原因'', ADD COLUMN urgency VARCHAR(50) DEFAULT NULL COMMENT ''紧急程度'', ADD COLUMN notify_owner_sales TINYINT DEFAULT 1 COMMENT ''是否提醒归属销售'', ADD COLUMN pause_ai TINYINT DEFAULT 1 COMMENT ''是否暂停AI'', ADD COLUMN silent_to_customer TINYINT DEFAULT 1 COMMENT ''客户侧是否无感'', ADD COLUMN create_follow_task TINYINT DEFAULT 1 COMMENT ''是否创建跟进待办'', ADD COLUMN notify_status VARCHAR(30) DEFAULT ''pending'' COMMENT ''提醒状态 pending/sent/failed/unassigned'', ADD COLUMN owner_sales_id VARCHAR(64) DEFAULT NULL COMMENT ''客户归属销售ID'', ADD COLUMN follow_task_id BIGINT DEFAULT NULL COMMENT ''跟进待办ID''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_learning_corpus :: missing [quality_score]
+SET @_tbl = 'lobster_learning_corpus';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'quality_score') = 0,
+    'ALTER TABLE `lobster_learning_corpus` ADD COLUMN `quality_score`     INT          DEFAULT 0 COMMENT ''语料质量分(0-100)''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_node_execution_log :: missing [quality_score, dimension_scores, regen_triggered, original_score, regenerated_score, scene_code, node_code]
+SET @_tbl = 'lobster_node_execution_log';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'quality_score') = 0,
+    'ALTER TABLE `lobster_node_execution_log` ADD COLUMN `quality_score` int DEFAULT NULL, ADD COLUMN `dimension_scores` JSON DEFAULT NULL COMMENT ''5维度分数JSON'', ADD COLUMN `regen_triggered` tinyint DEFAULT 0 COMMENT ''是否触发重生成'', ADD COLUMN `original_score` int DEFAULT NULL COMMENT ''原始评分'', ADD COLUMN `regenerated_score` int DEFAULT NULL COMMENT ''重生成评分'', ADD COLUMN `scene_code` varchar(64) DEFAULT NULL COMMENT ''AI场景编码'', ADD COLUMN `node_code` varchar(64) DEFAULT NULL COMMENT ''节点编码''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_node_interaction :: missing [workflow_id]
+SET @_tbl = 'lobster_node_interaction';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'workflow_id') = 0,
+    'ALTER TABLE `lobster_node_interaction` ADD COLUMN `workflow_id` bigint DEFAULT NULL COMMENT ''执行工作流ID''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_sales_corpus :: missing [content, source, usage_count]
+SET @_tbl = 'lobster_sales_corpus';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'content') = 0,
+    'ALTER TABLE `lobster_sales_corpus` ADD COLUMN `content` text, ADD COLUMN `source` varchar(50) DEFAULT NULL, ADD COLUMN `usage_count` int DEFAULT ''0''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: lobster_user_profile :: missing [consumption_power, channel_preference, churn_risk, customer_persona, behavioral_tags, churn_reason, next_action, last_order_time]
+SET @_tbl = 'lobster_user_profile';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'consumption_power') = 0,
+    'ALTER TABLE `lobster_user_profile` ADD COLUMN `consumption_power` VARCHAR(32) DEFAULT NULL COMMENT ''消费力等级'', ADD COLUMN `channel_preference` VARCHAR(64) DEFAULT NULL COMMENT ''渠道偏好'', ADD COLUMN `churn_risk` VARCHAR(16) DEFAULT NULL COMMENT ''流失风险 低/中/高'', ADD COLUMN `customer_persona` VARCHAR(128) DEFAULT NULL COMMENT ''客户人设'', ADD COLUMN `behavioral_tags` VARCHAR(500) DEFAULT NULL COMMENT ''行为标签'', ADD COLUMN `churn_reason` VARCHAR(500) DEFAULT NULL COMMENT ''流失原因'', ADD COLUMN `next_action` VARCHAR(500) DEFAULT NULL COMMENT ''下一步运营动作'', ADD COLUMN `last_order_time` DATETIME DEFAULT NULL COMMENT ''最近下单时间''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: qw_external_contact :: missing [ipad_user_id]
+SET @_tbl = 'qw_external_contact';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'ipad_user_id') = 0,
+    'ALTER TABLE `qw_external_contact` ADD COLUMN `ipad_user_id` bigint NULL DEFAULT NULL COMMENT ''iPad用户ID''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: qw_user :: missing [acc_id]
+SET @_tbl = 'qw_user';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'acc_id') = 0,
+    'ALTER TABLE `qw_user` ADD COLUMN `acc_id`           varchar(100)   NULL DEFAULT NULL COMMENT ''企微用户账号ID''',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+
+-- table: wx_contact :: missing [friends, wx_remark]
+SET @_tbl = 'wx_contact';
+SET @_sql = (SELECT IF((SELECT COUNT(1) FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = @_tbl AND COLUMN_NAME = 'friends') = 0,
+    'ALTER TABLE `wx_contact` ADD COLUMN `friends`           int NULL DEFAULT 1 COMMENT ''是否是好友1是0否'', ADD COLUMN `wx_remark`         varchar(255)  NULL DEFAULT NULL',
+    'SELECT 1'));
+PREPARE stmt FROM @_sql; EXECUTE stmt; DEALLOCATE PREPARE stmt;
+

+ 3 - 2
java/sql/admin_ai_model_config.sql

@@ -18,6 +18,7 @@ CREATE TABLE admin_ai_model (
     temperature DOUBLE DEFAULT 0.7 COMMENT '温度参数',
     sort_order INT DEFAULT 0 COMMENT '全局排序,越小越优先',
     status TINYINT DEFAULT 1 COMMENT '状态: 1启用 0禁用',
+    model_type INT DEFAULT 0 COMMENT '模型类型,0:默认 1:租户销售端使用生成视频的模型',
     create_by VARCHAR(50) DEFAULT 'system' COMMENT '创建人',
     create_time DATETIME DEFAULT NOW() COMMENT '创建时间',
     update_by VARCHAR(50) DEFAULT 'system' COMMENT '更新人',
@@ -60,7 +61,7 @@ CREATE TABLE admin_ai_scene_model (
 
 -- 从 company_ai_provider 迁移现有模型(如存在)
 INSERT INTO admin_ai_model (model_name, provider_code, model_identifier, api_endpoint, api_key, embedding_endpoint, max_tokens, temperature, sort_order, status)
-SELECT 
+SELECT
     provider_name,
     provider_code,
     model_name,
@@ -79,7 +80,7 @@ WHERE del_flag = 0 AND enabled = 1;
 -- 如果上述迁移没有数据,插入默认4种模型预设
 INSERT INTO admin_ai_model (model_name, provider_code, model_identifier, api_endpoint, api_key, embedding_endpoint, max_tokens, temperature, sort_order, status)
 SELECT * FROM (
-    SELECT '豆包(Doubao)' AS model_name, 'doubao' AS provider_code, 'doubao-pro-32k' AS model_identifier, 
+    SELECT '豆包(Doubao)' AS model_name, 'doubao' AS provider_code, 'doubao-pro-32k' AS model_identifier,
            'https://ark.cn-beijing.volces.com/api/v3/chat/completions' AS api_endpoint, '' AS api_key,
            'https://ark.cn-beijing.volces.com/api/v3/embeddings' AS embedding_endpoint,
            4096 AS max_tokens, 0.7 AS temperature, 0 AS sort_order, 1 AS status

+ 16 - 3
ylrz-doctorui/src/api/login.js

@@ -1,14 +1,16 @@
 import request from '@/utils/request'
 
-// 医生/药师登录(type: 1医生 2药师)
-export function login(account, password, type, tenantCode) {
+// 医生/药师登录(type: 1医生 2药师,Web 端带验证码
+export function login(account, password, type, code, uuid, tenantCode) {
   return request({
-    url: '/app/doctor/login',
+    url: '/app/doctor/loginByWeb',
     method: 'post',
     data: {
       account,
       password,
       type,
+      code,
+      uuid,
       tenantCode
     }
   })
@@ -21,3 +23,14 @@ export function getInfo() {
     method: 'get'
   })
 }
+
+// 获取验证码
+export function getCodeImg() {
+  return request({
+    url: '/app/common/captchaImage',
+    method: 'get',
+    headers: {
+      isToken: false
+    }
+  })
+}

+ 2 - 2
ylrz-doctorui/src/store/modules/user.js

@@ -25,9 +25,9 @@ const user = {
   actions: {
     // 登录(医生/药师,type: 1医生 2药师)
     Login({ commit }, userInfo) {
-      const { account, password, type, tenantCode } = userInfo
+      const { account, password, type, code, uuid, tenantCode } = userInfo
       return new Promise((resolve, reject) => {
-        login(account, password, type, tenantCode).then(res => {
+        login(account, password, type, code, uuid, tenantCode).then(res => {
           setToken(res.token)
           setTenantCode(tenantCode)
           commit('SET_TOKEN', res.token)

+ 39 - 4
ylrz-doctorui/src/views/login.vue

@@ -21,6 +21,11 @@
             <el-radio-button label="2">药师</el-radio-button>
           </el-radio-group>
         </el-form-item>
+        <el-form-item prop="tenantCode">
+          <el-input v-model="loginForm.tenantCode" type="text" auto-complete="off" placeholder="请输入企业编号(如 test001)" class="tenantCode" @keyup.enter.native="handleLogin">
+            <img slot="prefix" src="../assets/images/user.png" class="input-icon "/>
+          </el-input>
+        </el-form-item>
         <el-form-item prop="account">
           <el-input v-model="loginForm.account" type="text" auto-complete="off" placeholder="账号" class="username">
             <img slot="prefix" src="../assets/images/user.png" class="input-icon "/>
@@ -40,10 +45,20 @@
             <img slot="suffix" src="../assets/images/eyeopen.png" class="input-icon2" v-else @click.stop="changetype()"/>
           </el-input>
         </el-form-item>
-        <el-form-item prop="tenantCode">
-          <el-input v-model="loginForm.tenantCode" type="text" auto-complete="off" placeholder="请输入企业编号(如 test001)" class="tenantCode" @keyup.enter.native="handleLogin">
-            <img slot="prefix" src="../assets/images/user.png" class="input-icon "/>
+        <el-form-item prop="code" v-if="captchaOnOff">
+          <el-input
+            v-model="loginForm.code"
+            auto-complete="off"
+            placeholder="验证码"
+            style="width: 63%"
+            @keyup.enter.native="handleLogin"
+
+          >
+            <img slot="prefix" src="../assets/images/code.png" class="input-icon" />
           </el-input>
+          <div class="login-code">
+            <img :src="codeUrl" @click="getCode" class="login-code-img"/>
+          </div>
         </el-form-item>
         <el-form-item style="width:100%;">
           <el-button
@@ -69,6 +84,8 @@
 </template>
 
 <script>
+import { getCodeImg } from "@/api/login";
+
 export default {
   name: "Login",
   data() {
@@ -77,6 +94,8 @@ export default {
       companyName: process.env.VUE_APP_COMPANY_NAME,
       icpRecord: process.env.VUE_APP_ICP_RECORD,
       icpUrl: process.env.VUE_APP_ICP_URL,
+      codeUrl: "",
+      captchaOnOff: true,
       loading: false,
       redirect: undefined,
       passwordtype: 'password',
@@ -85,6 +104,8 @@ export default {
         type: '1',
         account: '',
         password: '',
+        code: '',
+        uuid: '',
         tenantCode: ''
       },
       loginRules: {
@@ -93,7 +114,8 @@ export default {
           { required: true, trigger: "blur", message: "密码不能为空" },
           { min: 5, max: 20, message: "密码长度为5-20位", trigger: ["blur", "change"] }
         ],
-        tenantCode: [{ required: true, trigger: "blur", message: "租户编码不能为空" }]
+        tenantCode: [{ required: true, trigger: "blur", message: "租户编码不能为空" }],
+        code: [{ required: true, trigger: "change", message: "验证码不能为空" }]
       }
     };
   },
@@ -105,7 +127,19 @@ export default {
       immediate: true
     }
   },
+  created() {
+    this.getCode();
+  },
   methods: {
+    getCode() {
+      getCodeImg().then(res => {
+        this.captchaOnOff = res.captchaOnOff === undefined ? true : res.captchaOnOff;
+        if (this.captchaOnOff) {
+          this.codeUrl = "data:image/gif;base64," + res.img;
+          this.loginForm.uuid = res.uuid;
+        }
+      });
+    },
     handleLogin() {
       this.$refs.loginForm.validate(valid => {
         if (valid) {
@@ -117,6 +151,7 @@ export default {
             })
             .catch(() => {
               this.loading = false;
+              this.getCode();
             });
         }
       });

部分文件因文件數量過多而無法顯示