Просмотр исходного кода

Merge branch 'dev' of http://192.168.0.3:3000/CRBC-MaaS-Platform-Project/LQAdminPlatform into dev

LuoChinWen 1 месяц назад
Родитель
Сommit
57b1c2dd27
38 измененных файлов с 7177 добавлено и 1699 удалено
  1. 3 0
      .gitignore
  2. 0 101
      logs/lq-admin-app.log.1
  3. 0 1074
      logs/lq-admin-app.log.5
  4. 2199 0
      scripts/base_data_20260301.sql
  5. 785 0
      scripts/lq_db_dev_20260301.sql
  6. 11 0
      scripts/t_sys_dict_category.sql
  7. 44 0
      scripts/t_sys_dict_category_create_2026_02_22.sql
  8. 102 0
      scripts/t_sys_dict_category_item.sql
  9. 131 0
      scripts/t_sys_dict_init_data_2026_02_22.sql
  10. 337 0
      scripts/t_sys_menu_dict_insert_2026_02_22.sql
  11. 2 2
      src/app/config/config.ini
  12. 2 1
      src/app/sample/models/metadata.py
  13. 2 1
      src/app/sample/schemas/knowledge_base.py
  14. 4 0
      src/app/sample/schemas/sample_schemas.py
  15. 1 1
      src/app/schemas/base.py
  16. 4 0
      src/app/server/app.py
  17. 308 0
      src/app/services/dict_category_service.py
  18. 348 0
      src/app/services/dict_item_service.py
  19. 107 8
      src/app/services/image_service.py
  20. 42 15
      src/app/services/knowledge_base_service.py
  21. 19 12
      src/app/services/milvus_service.py
  22. 663 124
      src/app/services/sample_service.py
  23. 3 3
      src/app/services/snippet_service.py
  24. 100 69
      src/app/services/task_service.py
  25. 60 0
      src/app/system/schemas/dict_category_schema.py
  26. 61 0
      src/app/system/schemas/dict_item_schema.py
  27. 10 10
      src/views/auth_view.py
  28. 229 0
      src/views/dict_category_view.py
  29. 274 0
      src/views/dict_item_view.py
  30. 68 24
      src/views/image_view.py
  31. 16 16
      src/views/knowledge_base_view.py
  32. 93 73
      src/views/sample_view.py
  33. 8 8
      src/views/search_engine_view.py
  34. 7 7
      src/views/snippet_view.py
  35. 128 128
      src/views/system_view.py
  36. 22 22
      src/views/tag_view.py
  37. 669 0
      项目/API接口定义/字典管理API接口.md
  38. 315 0
      项目/字典管理系统设计.md

+ 3 - 0
.gitignore

@@ -49,13 +49,16 @@ coverage.xml
 *,cover
 
 # Translations
+logs/
 *.mo
 *.pot
 *.pdf
 *.docs
 *.doc
 # Django stuff:
+*.log.*
 *.log
+
 langfuse/
 # Sphinx documentation
 docs/_build/

Разница между файлами не показана из-за своего большого размера
+ 0 - 101
logs/lq-admin-app.log.1


Разница между файлами не показана из-за своего большого размера
+ 0 - 1074
logs/lq-admin-app.log.5


Разница между файлами не показана из-за своего большого размера
+ 2199 - 0
scripts/base_data_20260301.sql


Разница между файлами не показана из-за своего большого размера
+ 785 - 0
scripts/lq_db_dev_20260301.sql


+ 11 - 0
scripts/t_sys_dict_category.sql

@@ -0,0 +1,11 @@
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('3738262c-d810-4bc1-8422-0417cf630e4c', 'lq-kngb', '路桥知识库字典', '路桥知识库枚举定义', '0', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:55:40', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:58:32', '0', '1');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('9b51168d-1050-45fd-a17e-59a8776eff72', 'CSAS', '施工标准规范', '施工标准规范', '3738262c-d810-4bc1-8422-0417cf630e4c', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:57:33', NULL, '2026-02-23 14:57:33', '0', '1');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('construction_plan_first_type', 'construction_plan_first_type', '施工方案一级分类枚举定义', '施工方案一级分类枚举定义', 'effb2f83-3322-445f-aff4-6e5e1c3a7691', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 15:00:38', '0', '2');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('construction_plan_fourth_type', 'construction_plan_fourth_type', '施工方案四级分类枚举定义', '施工方案四级分类枚举定义', 'effb2f83-3322-445f-aff4-6e5e1c3a7691', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:59:01', '0', '1');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('construction_plan_second_type', 'construction_plan_second_type', '施工方案二级分类枚举定义', '施工方案二级分类枚举定义', 'effb2f83-3322-445f-aff4-6e5e1c3a7691', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 15:00:53', '0', '3');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('construction_plan_third_type', 'construction_plan_third_type', '施工方案三级分类枚举定义', '施工方案三级分类枚举定义', 'effb2f83-3322-445f-aff4-6e5e1c3a7691', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:59:19', '0', '1');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('construction_plan_type', 'construction_plan_type', '施工方案类别枚举定义', '施工方案类别枚举定义', 'effb2f83-3322-445f-aff4-6e5e1c3a7691', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 15:01:51', '0', '2');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('effb2f83-3322-445f-aff4-6e5e1c3a7691', 'CP', '施工方案枚举字典', '施工方案枚举字典', '3738262c-d810-4bc1-8422-0417cf630e4c', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:58:19', NULL, '2026-02-23 14:58:19', '0', NULL);
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('file_type', 'file_type', '文件类型枚举定义', '文件类型枚举定义', '9b51168d-1050-45fd-a17e-59a8776eff72', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:59:35', '0', '1');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('professional_type', 'professional_type', '专业类型枚举定义', '专业类型枚举定义', '9b51168d-1050-45fd-a17e-59a8776eff72', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:59:46', '0', '1');
+INSERT INTO `t_sys_dict_category`(`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `created_by`, `created_time`, `updated_by`, `updated_time`, `del_flag`, `category_level`) VALUES ('time_effect', 'time_effect', '时效性枚举定义', '时效性枚举定义', '9b51168d-1050-45fd-a17e-59a8776eff72', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 14:59:53', '0', '1');

+ 44 - 0
scripts/t_sys_dict_category_create_2026_02_22.sql

@@ -0,0 +1,44 @@
+-- 字典管理系统表结构创建脚本
+-- 创建日期: 2026-02-22
+
+-- 字典类型表
+DROP TABLE IF EXISTS `t_sys_dict_category`;
+CREATE TABLE `t_sys_dict_category` (
+  `category_id` varchar(36) NOT NULL COMMENT '字典类型id',
+  `category_no` varchar(512) DEFAULT NULL COMMENT '字典类型编号',
+  `category_name` varchar(512) NOT NULL COMMENT '字典类型名称',
+  `category_desc` varchar(512) DEFAULT NULL COMMENT '字典类型备注',
+  `parent_field` varchar(36) NOT NULL COMMENT '父节点字典类型id',
+  `created_by` varchar(36) DEFAULT NULL COMMENT '创建人',
+  `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间,默认当前时间',
+  `updated_by` varchar(36) DEFAULT NULL COMMENT '修改人',
+  `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间,默认当前时间',
+  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志位:1是0否',
+  `category_level` char(1) DEFAULT NULL COMMENT '字典层级',
+  PRIMARY KEY (`category_id`) USING BTREE
+) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='字典类别表';
+
+-- 字典项表
+DROP TABLE IF EXISTS `t_sys_dict_category_item`;
+CREATE TABLE `t_sys_dict_category_item` (
+  `dict_id` int NOT NULL AUTO_INCREMENT COMMENT '字典id',
+  `dict_name` varchar(512) NOT NULL COMMENT '字典名称',
+  `dict_value` varchar(512) NOT NULL COMMENT '字典值',
+  `dict_desc` varchar(512) DEFAULT NULL COMMENT '字典备注',
+  `category_id` varchar(255) NOT NULL COMMENT '字典类型id',
+  `created_by` varchar(36) DEFAULT NULL COMMENT '创建人',
+  `created_time` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间,默认当前时间',
+  `updated_by` varchar(36) DEFAULT NULL COMMENT '修改人',
+  `updated_time` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间,默认当前时间',
+  `enable_flag` char(1) NOT NULL DEFAULT '1' COMMENT '启用标志位:1启用0禁用',
+  `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '删除标志位:1是0否',
+  `sort` int DEFAULT NULL COMMENT '排序',
+  PRIMARY KEY (`dict_id`) USING BTREE
+) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='字典项表';
+
+
+
+
+--  知识库元数据信息表 添加字典类型id字段
+ALTER TABLE t_samp_metadata
+  ADD COLUMN `category_id` varchar(50)  COMMENT '字典类型id' AFTER field_type;

+ 102 - 0
scripts/t_sys_dict_category_item.sql

@@ -0,0 +1,102 @@
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (1, '国家标准', 'GB', 'National Standard', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 1);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (2, '行业标准', 'HY', 'Industry Standard', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 2);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (3, '部门规章', 'BM', 'Department Regulations', 'file_type', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 15:10:01', '1', '0', 4);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (4, '地方标准', 'DB', 'Local Standard', 'file_type', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 15:10:11', '1', '0', 5);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (5, '企业标准', 'QY', 'Enterprise Standard', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 5);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (6, '管理制度', 'GL', 'Management Regulation', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 6);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (7, '技术规范', 'GF', 'Technical Specification', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 7);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (8, '团体标准', 'TT', 'Group Standard', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 8);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (9, '国际标准', 'GJ', 'International Standard', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 9);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (10, '国家法律', 'FL', 'National Law', 'file_type', NULL, '2026-02-23 13:57:00', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 15:09:54', '1', '0', 3);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (11, '地方法规', 'LR', 'Local Regulations', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 11);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (12, '其他', 'QT', 'Other', 'file_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 12);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (13, '法律法规', 'FL', 'Legal Regulations', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 1);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (14, '通用标准', 'TY', 'General Standards', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 2);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (15, '勘察钻探', 'KC', 'Survey and Drilling', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 3);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (16, '地基基础', 'DJ', 'Foundation', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 4);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (17, '路基路面', 'LJ', 'Roadbed and Pavement', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 5);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (18, '桥梁工程', 'QL', 'Bridge Engineering', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 6);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (19, '隧道工程', 'SD', 'Tunnel Engineering', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 7);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (20, '交通工程', 'JT', 'Traffic Engineering', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 8);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (21, '建筑工程', 'JZ', 'Construction Engineering', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 9);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (22, '市政工程', 'SZ', 'Municipal Engineering', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 10);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (23, '机电安装', 'JD', 'Electromechanical Installation', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 11);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (24, '路桥工程', 'LB', 'Road and Bridge Engineering', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 12);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (25, '装饰装修', 'ZS', 'Decoration and Renovation', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 13);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (26, '港口航道', 'GK', 'Port and Waterway', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 14);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (27, '铁路工程', 'TL', 'Railway Engineering', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 15);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (28, '房建工程', 'FJ', 'Building Engineering', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 16);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (29, '水利电力', 'SL', 'Water Conservancy and Power', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 17);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (30, '信息化', 'XX', 'Information Technology', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 18);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (31, '试验检测', 'SY', 'Testing and Inspection', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 19);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (32, '安全环保', 'AQ', 'Safety and Environmental Protection', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 20);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (33, '其他', 'QT', 'Other', 'professional_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 21);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (34, '现行', 'XH', 'Current', 'time_effect', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 1);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (35, '废止', 'FZ', 'Abolished', 'time_effect', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 2);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (36, '试行', 'SX', 'Trial / Interim', 'time_effect', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 3);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (37, '超危大方案', 'CH', 'Ultra-High-Risk Construction Plan', 'construction_plan_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 1);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (38, '超危大方案较大Ⅱ级', 'CH2', 'Ultra-High-Risk Plan – Level II (Major)', 'construction_plan_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 2);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (39, '超危大方案特大Ⅳ级', 'CH4', 'Ultra-High-Risk Plan – Level IV (Extra Large)', 'construction_plan_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 3);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (40, '超危大方案一般Ⅰ级', 'CH1', 'Ultra-High-Risk Plan – Level I (General)', 'construction_plan_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 4);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (41, '超危大方案重大Ⅲ级', 'CH3', 'Ultra-High-Risk Plan – Level III (Significant)', 'construction_plan_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 5);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (42, '危大方案', 'WD', 'High-Risk Construction Plan', 'construction_plan_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 6);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (43, '一般方案', 'YB', 'General Construction Plan', 'construction_plan_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 7);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (44, '其他', 'QT', 'Other', 'construction_plan_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 8);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (45, '施工方案', 'SC', 'Construction Plan', 'construction_plan_first_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 1);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (46, '其他', 'QT', 'Other', 'construction_plan_first_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 2);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (47, '临建工程', 'LZ', 'Temporary Construction', 'construction_plan_second_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 1);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (48, '路基工程', 'LJ', 'Subgrade Engineering', 'construction_plan_second_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 2);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (49, '桥梁工程', 'QL', 'Bridge Engineering', 'construction_plan_second_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 3);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (50, '隧道工程', 'SD', 'Tunnel Engineering', 'construction_plan_second_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 4);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (51, '其他', 'QT', 'Other', 'construction_plan_second_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 5);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (52, 'TBM施工', 'TM', 'TBM Construction', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 1);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (53, '拌和站安、拆施工', 'BH', 'Mixing Plant Installation & Dismantling', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 2);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (54, '不良地质隧道施工', 'BL', 'Construction in Poor Geological Tunnel', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 3);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (55, '常规桥梁', 'CG', 'Conventional Bridge', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 4);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (56, '挡土墙工程类', 'DT', 'Retaining Wall Engineering', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 5);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (57, '辅助坑道施工', 'FB', 'Auxiliary Adit Construction', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 6);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (58, '复杂洞口工程施工', 'FD', 'Complex Portal Construction', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 7);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (59, '钢筋加工场安、拆', 'GG', 'Reinforcement Yard Setup & Removal', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 8);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (60, '钢栈桥施工', 'GZ', 'Steel Trestle Construction', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 9);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (61, '拱桥', 'GH', 'Arch Bridge', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 10);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (62, '涵洞工程类', 'HD', 'Culvert Engineering', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 11);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (63, '滑坡体处理类', 'HP', 'Landslide Treatment', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 12);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (64, '路堤', 'LT', 'Embankment', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 13);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (65, '路堑', 'LQ', 'Cut Slope', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 14);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (66, '其他', 'QT', 'Other', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 15);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (67, '深基坑', 'JK', 'Deep Foundation Pit', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 16);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (68, '隧道总体施工', 'ZT', 'Overall Tunnel Construction', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 17);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (69, '特殊结构隧道', 'TS', 'Special Structure Tunnel', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 18);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (70, '斜拉桥', 'XL', 'Cable-Stayed Bridge', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 19);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (71, '悬索桥', 'XS', 'Suspension Bridge', 'construction_plan_third_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 20);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (72, '挡土墙', 'DT', 'Retaining Wall', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 1);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (73, '顶管', 'DG', 'Pipe Jacking', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 2);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (74, '断层破碎带及软弱围岩', 'DL', 'Fault Zone & Weak Rock Mass', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 3);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (75, '钢筋砼箱涵', 'GX', 'Reinforced Concrete Box Culvert', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 4);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (76, '高填路堤', 'GT', 'High Fill Embankment', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 5);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (77, '抗滑桩', 'KH', 'Anti-Slide Pile', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 6);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (78, '软岩大变形隧道', 'RY', 'Large Deformation Tunnel in Soft Rock', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 7);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (79, '上部结构', 'SB', 'Superstructure', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 8);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (80, '深基坑开挖与支护', 'JK', 'Deep Excavation & Support', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 9);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (81, '深挖路堑', 'LC', 'Deep Road Cut', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 10);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (82, '隧道TBM', 'TM', 'Tunnel Boring Machine (TBM)', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 11);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (83, '隧道进洞', 'JD', 'Tunnel Portal Entry', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 12);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (84, '隧道竖井', 'SJ', 'Tunnel Shaft', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 13);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (85, '隧道斜井', 'XJ', 'Tunnel Incline Shaft', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 14);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (86, '特种设备', 'TZ', 'Special Equipment', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 15);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (87, '瓦斯隧道', 'WS', 'Gas-Prone Tunnel', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 16);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (88, '下部结构', 'XB', 'Substructure', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 17);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (89, '小净距隧道', 'NJ', 'Small Spacing Tunnel', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 18);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (90, '岩爆隧道', 'YB', 'Rockburst Tunnel', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 19);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (91, '岩溶隧道', 'YR', 'Karst Tunnel', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 20);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (92, '涌水突泥隧道', 'YN', 'Water Inrush & Mud Burst Tunnel', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 21);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (93, '桩基础', 'ZJ', 'Pile Foundation', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 22);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (94, '其他', 'QT', 'Other', 'construction_plan_fourth_type', NULL, '2026-02-23 13:57:00', NULL, '2026-02-23 13:57:00', '1', '0', 23);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (95, '其它1', 'other', '其它1', 'file_type', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 16:32:15', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 16:32:41', '1', '0', 13);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (96, '其它1', 'other', '其它1', 'construction_plan_first_type', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 16:38:59', NULL, '2026-02-23 16:38:59', '1', '0', 10);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (97, '其它1', 'other', '其它1', 'construction_plan_type', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 16:39:59', NULL, '2026-02-23 16:39:59', '1', '0', 10);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (98, '其它1', 'other', '其它1', 'professional_type', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 17:11:06', NULL, '2026-02-23 17:11:06', '1', '0', 25);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (99, '其它1', 'other', '其它1', 'time_effect', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 17:11:29', NULL, '2026-02-23 17:11:29', '1', '0', 10);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (100, '其它1', 'other', '其它1', 'construction_plan_fourth_type', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 17:11:58', NULL, '2026-02-23 17:11:58', '1', '0', 30);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (101, '其它1', 'other', '其它1', 'construction_plan_second_type', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 17:12:27', NULL, '2026-02-23 17:12:27', '1', '0', 10);
+INSERT INTO `t_sys_dict_category_item`(`dict_id`, `dict_name`, `dict_value`, `dict_desc`, `category_id`, `created_by`, `created_time`, `updated_by`, `updated_time`, `enable_flag`, `del_flag`, `sort`) VALUES (102, '其它1', 'other', '其它1', 'construction_plan_third_type', 'ed6a79d3-0083-4d81-8b48-fc522f686f74', '2026-02-23 17:12:53', NULL, '2026-02-23 17:12:53', '1', '0', 30);

+ 131 - 0
scripts/t_sys_dict_init_data_2026_02_22.sql

@@ -0,0 +1,131 @@
+-- 字典管理系统初始化数据脚本
+-- 创建日期: 2026-02-22
+
+-- 插入字典类型
+INSERT INTO `t_sys_dict_category` (`category_id`, `category_no`, `category_name`, `category_desc`, `parent_field`, `del_flag`, `category_level`) VALUES
+('file_type', 'file_type', '文件类型枚举定义', '文件类型枚举定义', '0', '0', '1'),
+('professional_type', 'professional_type', '专业类型枚举定义', '专业类型枚举定义', '0', '0', '1'),
+('time_effect', 'time_effect', '时效性枚举定义', '时效性枚举定义', '0', '0', '1'),
+('construction_plan_type', 'construction_plan_type', '施工方案类别枚举定义', '施工方案类别枚举定义', '0', '0', '1'),
+('construction_plan_first_type', 'construction_plan_first_type', '施工方案一级分类枚举定义', '施工方案一级分类枚举定义', '0', '0', '1'),
+('construction_plan_second_type', 'construction_plan_second_type', '施工方案二级分类枚举定义', '施工方案二级分类枚举定义', '0', '0', '1'),
+('construction_plan_third_type', 'construction_plan_third_type', '施工方案三级分类枚举定义', '施工方案三级分类枚举定义', '0', '0', '1'),
+('construction_plan_fourth_type', 'construction_plan_fourth_type', '施工方案四级分类枚举定义', '施工方案四级分类枚举定义', '0', '0', '1');
+
+-- 插入文件类型枚举定义字典项
+INSERT INTO `t_sys_dict_category_item` (`dict_name`, `dict_value`, `dict_desc`, `category_id`, `enable_flag`, `del_flag`, `sort`) VALUES
+('国家标准', 'GB', 'National Standard', 'file_type', '1', '0', 1),
+('行业标准', 'HY', 'Industry Standard', 'file_type', '1', '0', 2),
+('部门规章', 'BM', 'Department Regulations', 'file_type', '1', '0', 3),
+('地方标准', 'DB', 'Local Standard', 'file_type', '1', '0', 4),
+('企业标准', 'QY', 'Enterprise Standard', 'file_type', '1', '0', 5),
+('管理制度', 'GL', 'Management Regulation', 'file_type', '1', '0', 6),
+('技术规范', 'GF', 'Technical Specification', 'file_type', '1', '0', 7),
+('团体标准', 'TT', 'Group Standard', 'file_type', '1', '0', 8),
+('国际标准', 'GJ', 'International Standard', 'file_type', '1', '0', 9),
+('国家法律', 'FL', 'National Law', 'file_type', '1', '0', 10),
+('地方法规', 'LR', 'Local Regulations', 'file_type', '1', '0', 11),
+('其他', 'QT', 'Other', 'file_type', '1', '0', 12);
+
+-- 插入专业类型枚举定义字典项
+INSERT INTO `t_sys_dict_category_item` (`dict_name`, `dict_value`, `dict_desc`, `category_id`, `enable_flag`, `del_flag`, `sort`) VALUES
+('法律法规', 'FL', 'Legal Regulations', 'professional_type', '1', '0', 1),
+('通用标准', 'TY', 'General Standards', 'professional_type', '1', '0', 2),
+('勘察钻探', 'KC', 'Survey and Drilling', 'professional_type', '1', '0', 3),
+('地基基础', 'DJ', 'Foundation', 'professional_type', '1', '0', 4),
+('路基路面', 'LJ', 'Roadbed and Pavement', 'professional_type', '1', '0', 5),
+('桥梁工程', 'QL', 'Bridge Engineering', 'professional_type', '1', '0', 6),
+('隧道工程', 'SD', 'Tunnel Engineering', 'professional_type', '1', '0', 7),
+('交通工程', 'JT', 'Traffic Engineering', 'professional_type', '1', '0', 8),
+('建筑工程', 'JZ', 'Construction Engineering', 'professional_type', '1', '0', 9),
+('市政工程', 'SZ', 'Municipal Engineering', 'professional_type', '1', '0', 10),
+('机电安装', 'JD', 'Electromechanical Installation', 'professional_type', '1', '0', 11),
+('路桥工程', 'LB', 'Road and Bridge Engineering', 'professional_type', '1', '0', 12),
+('装饰装修', 'ZS', 'Decoration and Renovation', 'professional_type', '1', '0', 13),
+('港口航道', 'GK', 'Port and Waterway', 'professional_type', '1', '0', 14),
+('铁路工程', 'TL', 'Railway Engineering', 'professional_type', '1', '0', 15),
+('房建工程', 'FJ', 'Building Engineering', 'professional_type', '1', '0', 16),
+('水利电力', 'SL', 'Water Conservancy and Power', 'professional_type', '1', '0', 17),
+('信息化', 'XX', 'Information Technology', 'professional_type', '1', '0', 18),
+('试验检测', 'SY', 'Testing and Inspection', 'professional_type', '1', '0', 19),
+('安全环保', 'AQ', 'Safety and Environmental Protection', 'professional_type', '1', '0', 20),
+('其他', 'QT', 'Other', 'professional_type', '1', '0', 21);
+
+-- 插入时效性枚举定义字典项
+INSERT INTO `t_sys_dict_category_item` (`dict_name`, `dict_value`, `dict_desc`, `category_id`, `enable_flag`, `del_flag`, `sort`) VALUES
+('现行', 'XH', 'Current', 'time_effect', '1', '0', 1),
+('废止', 'FZ', 'Abolished', 'time_effect', '1', '0', 2),
+('试行', 'SX', 'Trial / Interim', 'time_effect', '1', '0', 3);
+
+-- 插入施工方案类别枚举定义字典项
+INSERT INTO `t_sys_dict_category_item` (`dict_name`, `dict_value`, `dict_desc`, `category_id`, `enable_flag`, `del_flag`, `sort`) VALUES
+('超危大方案', 'CH', 'Ultra-High-Risk Construction Plan', 'construction_plan_type', '1', '0', 1),
+('超危大方案较大Ⅱ级', 'CH2', 'Ultra-High-Risk Plan – Level II (Major)', 'construction_plan_type', '1', '0', 2),
+('超危大方案特大Ⅳ级', 'CH4', 'Ultra-High-Risk Plan – Level IV (Extra Large)', 'construction_plan_type', '1', '0', 3),
+('超危大方案一般Ⅰ级', 'CH1', 'Ultra-High-Risk Plan – Level I (General)', 'construction_plan_type', '1', '0', 4),
+('超危大方案重大Ⅲ级', 'CH3', 'Ultra-High-Risk Plan – Level III (Significant)', 'construction_plan_type', '1', '0', 5),
+('危大方案', 'WD', 'High-Risk Construction Plan', 'construction_plan_type', '1', '0', 6),
+('一般方案', 'YB', 'General Construction Plan', 'construction_plan_type', '1', '0', 7),
+('其他', 'QT', 'Other', 'construction_plan_type', '1', '0', 8);
+
+-- 插入施工方案一级分类枚举定义字典项
+INSERT INTO `t_sys_dict_category_item` (`dict_name`, `dict_value`, `dict_desc`, `category_id`, `enable_flag`, `del_flag`, `sort`) VALUES
+('施工方案', 'SC', 'Construction Plan', 'construction_plan_first_type', '1', '0', 1),
+('其他', 'QT', 'Other', 'construction_plan_first_type', '1', '0', 2);
+
+-- 插入施工方案二级分类枚举定义字典项
+INSERT INTO `t_sys_dict_category_item` (`dict_name`, `dict_value`, `dict_desc`, `category_id`, `enable_flag`, `del_flag`, `sort`) VALUES
+('临建工程', 'LZ', 'Temporary Construction', 'construction_plan_second_type', '1', '0', 1),
+('路基工程', 'LJ', 'Subgrade Engineering', 'construction_plan_second_type', '1', '0', 2),
+('桥梁工程', 'QL', 'Bridge Engineering', 'construction_plan_second_type', '1', '0', 3),
+('隧道工程', 'SD', 'Tunnel Engineering', 'construction_plan_second_type', '1', '0', 4),
+('其他', 'QT', 'Other', 'construction_plan_second_type', '1', '0', 5);
+
+-- 插入施工方案三级分类枚举定义字典项
+INSERT INTO `t_sys_dict_category_item` (`dict_name`, `dict_value`, `dict_desc`, `category_id`, `enable_flag`, `del_flag`, `sort`) VALUES
+('TBM施工', 'TM', 'TBM Construction', 'construction_plan_third_type', '1', '0', 1),
+('拌和站安、拆施工', 'BH', 'Mixing Plant Installation & Dismantling', 'construction_plan_third_type', '1', '0', 2),
+('不良地质隧道施工', 'BL', 'Construction in Poor Geological Tunnel', 'construction_plan_third_type', '1', '0', 3),
+('常规桥梁', 'CG', 'Conventional Bridge', 'construction_plan_third_type', '1', '0', 4),
+('挡土墙工程类', 'DT', 'Retaining Wall Engineering', 'construction_plan_third_type', '1', '0', 5),
+('辅助坑道施工', 'FB', 'Auxiliary Adit Construction', 'construction_plan_third_type', '1', '0', 6),
+('复杂洞口工程施工', 'FD', 'Complex Portal Construction', 'construction_plan_third_type', '1', '0', 7),
+('钢筋加工场安、拆', 'GG', 'Reinforcement Yard Setup & Removal', 'construction_plan_third_type', '1', '0', 8),
+('钢栈桥施工', 'GZ', 'Steel Trestle Construction', 'construction_plan_third_type', '1', '0', 9),
+('拱桥', 'GH', 'Arch Bridge', 'construction_plan_third_type', '1', '0', 10),
+('涵洞工程类', 'HD', 'Culvert Engineering', 'construction_plan_third_type', '1', '0', 11),
+('滑坡体处理类', 'HP', 'Landslide Treatment', 'construction_plan_third_type', '1', '0', 12),
+('路堤', 'LT', 'Embankment', 'construction_plan_third_type', '1', '0', 13),
+('路堑', 'LQ', 'Cut Slope', 'construction_plan_third_type', '1', '0', 14),
+('其他', 'QT', 'Other', 'construction_plan_third_type', '1', '0', 15),
+('深基坑', 'JK', 'Deep Foundation Pit', 'construction_plan_third_type', '1', '0', 16),
+('隧道总体施工', 'ZT', 'Overall Tunnel Construction', 'construction_plan_third_type', '1', '0', 17),
+('特殊结构隧道', 'TS', 'Special Structure Tunnel', 'construction_plan_third_type', '1', '0', 18),
+('斜拉桥', 'XL', 'Cable-Stayed Bridge', 'construction_plan_third_type', '1', '0', 19),
+('悬索桥', 'XS', 'Suspension Bridge', 'construction_plan_third_type', '1', '0', 20);
+
+-- 插入施工方案四级分类枚举定义字典项
+INSERT INTO `t_sys_dict_category_item` (`dict_name`, `dict_value`, `dict_desc`, `category_id`, `enable_flag`, `del_flag`, `sort`) VALUES
+('挡土墙', 'DT', 'Retaining Wall', 'construction_plan_fourth_type', '1', '0', 1),
+('顶管', 'DG', 'Pipe Jacking', 'construction_plan_fourth_type', '1', '0', 2),
+('断层破碎带及软弱围岩', 'DL', 'Fault Zone & Weak Rock Mass', 'construction_plan_fourth_type', '1', '0', 3),
+('钢筋砼箱涵', 'GX', 'Reinforced Concrete Box Culvert', 'construction_plan_fourth_type', '1', '0', 4),
+('高填路堤', 'GT', 'High Fill Embankment', 'construction_plan_fourth_type', '1', '0', 5),
+('抗滑桩', 'KH', 'Anti-Slide Pile', 'construction_plan_fourth_type', '1', '0', 6),
+('软岩大变形隧道', 'RY', 'Large Deformation Tunnel in Soft Rock', 'construction_plan_fourth_type', '1', '0', 7),
+('上部结构', 'SB', 'Superstructure', 'construction_plan_fourth_type', '1', '0', 8),
+('深基坑开挖与支护', 'JK', 'Deep Excavation & Support', 'construction_plan_fourth_type', '1', '0', 9),
+('深挖路堑', 'LC', 'Deep Road Cut', 'construction_plan_fourth_type', '1', '0', 10),
+('隧道TBM', 'TM', 'Tunnel Boring Machine (TBM)', 'construction_plan_fourth_type', '1', '0', 11),
+('隧道进洞', 'JD', 'Tunnel Portal Entry', 'construction_plan_fourth_type', '1', '0', 12),
+('隧道竖井', 'SJ', 'Tunnel Shaft', 'construction_plan_fourth_type', '1', '0', 13),
+('隧道斜井', 'XJ', 'Tunnel Incline Shaft', 'construction_plan_fourth_type', '1', '0', 14),
+('特种设备', 'TZ', 'Special Equipment', 'construction_plan_fourth_type', '1', '0', 15),
+('瓦斯隧道', 'WS', 'Gas-Prone Tunnel', 'construction_plan_fourth_type', '1', '0', 16),
+('下部结构', 'XB', 'Substructure', 'construction_plan_fourth_type', '1', '0', 17),
+('小净距隧道', 'NJ', 'Small Spacing Tunnel', 'construction_plan_fourth_type', '1', '0', 18),
+('岩爆隧道', 'YB', 'Rockburst Tunnel', 'construction_plan_fourth_type', '1', '0', 19),
+('岩溶隧道', 'YR', 'Karst Tunnel', 'construction_plan_fourth_type', '1', '0', 20),
+('涌水突泥隧道', 'YN', 'Water Inrush & Mud Burst Tunnel', 'construction_plan_fourth_type', '1', '0', 21),
+('桩基础', 'ZJ', 'Pile Foundation', 'construction_plan_fourth_type', '1', '0', 22),
+('其他', 'QT', 'Other', 'construction_plan_fourth_type', '1', '0', 23);

+ 337 - 0
scripts/t_sys_menu_dict_insert_2026_02_22.sql

@@ -0,0 +1,337 @@
+-- 字典管理菜单配置SQL脚本
+-- 创建日期: 2026-02-22
+-- 说明: 在系统管理模块下添加字典管理菜单及相关按钮权限
+
+-- ==================== 字典管理主菜单 ====================
+-- 在系统管理(admin-main)下添加字典管理菜单
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-management',                    -- id: 菜单ID
+  'admin-main',                         -- parent_id: 父菜单ID(系统管理)
+  'dict-management',                    -- name: 菜单名称
+  '字典管理',                           -- title: 菜单标题
+  '/admin/dictionary',                  -- path: 菜单路径
+  'admin/Dictionary',                   -- component: 组件路径
+  'Collection',                         -- icon: 菜单图标(使用Collection图标)
+  8,                                    -- sort_order: 排序顺序(在应用管理之后)
+  'menu',                               -- menu_type: 菜单类型
+  0,                                    -- is_hidden: 是否隐藏
+  1,                                    -- is_active: 是否启用
+  '系统字典类型和字典项管理',          -- description: 菜单描述
+  'system',                             -- created_by: 创建人
+  NOW(),                                -- created_time: 创建时间
+  'system',                             -- updated_by: 修改人
+  NOW()                                 -- updated_time: 修改时间
+);
+
+-- ==================== 字典类型管理按钮权限 ====================
+
+-- 1. 查看字典类型
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-category-view-btn',
+  'dict-management',
+  'dict-category-view',
+  '查看字典类型',
+  NULL,
+  NULL,
+  'View',
+  1,
+  'button',
+  0,
+  1,
+  '查看字典类型树形结构和列表',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 2. 新增字典类型
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-category-create-btn',
+  'dict-management',
+  'dict-category-create',
+  '新增字典类型',
+  NULL,
+  NULL,
+  'Plus',
+  2,
+  'button',
+  0,
+  1,
+  '创建新的字典类型',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 3. 编辑字典类型
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-category-edit-btn',
+  'dict-management',
+  'dict-category-edit',
+  '编辑字典类型',
+  NULL,
+  NULL,
+  'Edit',
+  3,
+  'button',
+  0,
+  1,
+  '修改字典类型信息',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 4. 删除字典类型
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-category-delete-btn',
+  'dict-management',
+  'dict-category-delete',
+  '删除字典类型',
+  NULL,
+  NULL,
+  'Delete',
+  4,
+  'button',
+  0,
+  1,
+  '删除字典类型(逻辑删除)',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- ==================== 字典项管理按钮权限 ====================
+
+-- 5. 查看字典项
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-item-view-btn',
+  'dict-management',
+  'dict-item-view',
+  '查看字典项',
+  NULL,
+  NULL,
+  'View',
+  5,
+  'button',
+  0,
+  1,
+  '查看字典项列表和详情',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 6. 新增字典项
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-item-create-btn',
+  'dict-management',
+  'dict-item-create',
+  '新增字典项',
+  NULL,
+  NULL,
+  'Plus',
+  6,
+  'button',
+  0,
+  1,
+  '创建新的字典项',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 7. 编辑字典项
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-item-edit-btn',
+  'dict-management',
+  'dict-item-edit',
+  '编辑字典项',
+  NULL,
+  NULL,
+  'Edit',
+  7,
+  'button',
+  0,
+  1,
+  '修改字典项信息',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 8. 删除字典项
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-item-delete-btn',
+  'dict-management',
+  'dict-item-delete',
+  '删除字典项',
+  NULL,
+  NULL,
+  'Delete',
+  8,
+  'button',
+  0,
+  1,
+  '删除字典项(逻辑删除)',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 9. 批量删除字典项
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-item-batch-delete-btn',
+  'dict-management',
+  'dict-item-batch-delete',
+  '批量删除字典项',
+  NULL,
+  NULL,
+  'Delete',
+  9,
+  'button',
+  0,
+  1,
+  '批量删除多个字典项',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 10. 搜索字典项
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-item-search-btn',
+  'dict-management',
+  'dict-item-search',
+  '搜索字典项',
+  NULL,
+  NULL,
+  'Search',
+  10,
+  'button',
+  0,
+  1,
+  '按关键字和状态搜索字典项',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 11. 切换字典项状态
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-item-toggle-status-btn',
+  'dict-management',
+  'dict-item-toggle-status',
+  '切换字典项状态',
+  NULL,
+  NULL,
+  'Switch',
+  11,
+  'button',
+  0,
+  1,
+  '启用或禁用字典项',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 12. 导出字典数据(可选功能,预留)
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-export-btn',
+  'dict-management',
+  'dict-export',
+  '导出字典数据',
+  NULL,
+  NULL,
+  'Download',
+  12,
+  'button',
+  0,
+  1,
+  '导出字典类型和字典项数据',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- 13. 导入字典数据(可选功能,预留)
+INSERT INTO `t_sys_menu` VALUES (
+  'dict-import-btn',
+  'dict-management',
+  'dict-import',
+  '导入字典数据',
+  NULL,
+  NULL,
+  'Upload',
+  13,
+  'button',
+  0,
+  1,
+  '批量导入字典类型和字典项数据',
+  'system',
+  NOW(),
+  'system',
+  NOW()
+);
+
+-- ==================== 为超级管理员角色分配字典管理菜单权限 ====================
+-- 注意:需要根据实际的super_admin角色ID进行调整
+
+-- 获取super_admin角色ID(假设角色code为'super_admin')
+-- 为super_admin角色分配字典管理主菜单
+INSERT INTO `t_sys_role_menu` (id, role_id, menu_id, created_by, created_time)
+SELECT 
+  UUID(),
+  r.id,
+  'dict-management',
+  'system',
+  NOW()
+FROM t_sys_role r
+WHERE r.code = 'super_admin' OR r.name = 'super_admin'
+AND NOT EXISTS (
+  SELECT 1 FROM t_sys_role_menu rm 
+  WHERE rm.role_id = r.id AND rm.menu_id = 'dict-management'
+);
+
+-- 为super_admin角色分配所有字典管理按钮权限
+INSERT INTO `t_sys_role_menu` (id, role_id, menu_id, created_by, created_time)
+SELECT 
+  UUID(),
+  r.id,
+  m.id,
+  'system',
+  NOW()
+FROM t_sys_role r
+CROSS JOIN t_sys_menu m
+WHERE (r.code = 'super_admin' OR r.name = 'super_admin')
+AND m.parent_id = 'dict-management'
+AND m.menu_type = 'button'
+AND NOT EXISTS (
+  SELECT 1 FROM t_sys_role_menu rm 
+  WHERE rm.role_id = r.id AND rm.menu_id = m.id
+);
+
+-- ==================== 验证SQL ====================
+-- 查询字典管理菜单及其子菜单
+-- SELECT * FROM t_sys_menu WHERE id = 'dict-management' OR parent_id = 'dict-management' ORDER BY sort_order;
+
+-- 查询super_admin角色的字典管理权限
+-- SELECT m.* FROM t_sys_menu m
+-- JOIN t_sys_role_menu rm ON m.id = rm.menu_id
+-- JOIN t_sys_role r ON rm.role_id = r.id
+-- WHERE (r.code = 'super_admin' OR r.name = 'super_admin')
+-- AND (m.id = 'dict-management' OR m.parent_id = 'dict-management')
+-- ORDER BY m.sort_order;

+ 2 - 2
src/app/config/config.ini

@@ -37,7 +37,7 @@ REFRESH_TOKEN_EXPIRE_DAYS=30
 JWT_SECRET_KEY=dev-jwt-secret-key-change-in-production-12345678901234567890
 
 # 后台管理Token配置
-ADMIN_TOKEN_EXPIRE_MINUTES=3
+ADMIN_TOKEN_EXPIRE_MINUTES=20
 ADMIN_REFRESH_TOKEN_EXPIRE_HOURS=24
 
 # OAuth2配置
@@ -95,7 +95,7 @@ FILE_BASE_URL=http://192.168.91.15:19000/aidata/sampledata
 # MinERU 配置
 MINERU_ACCESS_KEY=
 MINERU_SECRET_KEY=
-MINERU_TOKEN=eyJ0eXBlIjoiSldUIiwiYWxnIjoiSFM1MTIifQ.eyJqdGkiOiI0MjcwMDM4NiIsInJvbCI6IlJPTEVfUkVHSVNURVIiLCJpc3MiOiJPcGVuWExhYiIsImlhdCI6MTc3MDM2MjIyNSwiY2xpZW50SWQiOiJsa3pkeDU3bnZ5MjJqa3BxOXgydyIsInBob25lIjoiIiwib3BlbklkIjpudWxsLCJ1dWlkIjoiYzgzOWVlYTAtYWZkOC00YTdjLWJmMTUtNTQ1YTU3ODQ2M2ZkIiwiZW1haWwiOiIiLCJleHAiOjE3NzE1NzE4MjV9.0DmDNrg7eSq8PxY043dyW08eKcIJOSOsVIDUx9oAmuMV1bQ6fMKBAXE1blL6mWyDn6B6jdbt3OESnVdNm3TqDQ
+MINERU_TOKEN=eyJ0eXBlIjoiSldUIiwiYWxnIjoiSFM1MTIifQ.eyJqdGkiOiI0MjcwMDM4NiIsInJvbCI6IlJPTEVfUkVHSVNURVIiLCJpc3MiOiJPcGVuWExhYiIsImlhdCI6MTc3MTg0MDYyOCwiY2xpZW50SWQiOiJsa3pkeDU3bnZ5MjJqa3BxOXgydyIsInBob25lIjoiIiwib3BlbklkIjpudWxsLCJ1dWlkIjoiZWRkZmIyNDctY2Q3Ni00YTFhLTk4OTMtODgxZWQ3ZGMyNzYzIiwiZW1haWwiOiIiLCJleHAiOjE3Nzk2MTY2Mjh9.63bhVtvVOJekpbh6Clprz9xlqA2lKAKTy0jswNjs77gRd-iTvtNLabP6YCm3pq4-miyYh946SHozGz4WAV-fBQ
 MINERU_API_APPLY=https://mineru.net/api/v4/file-urls/batch
 MINERU_API_BATCH_RESULT=https://mineru.net/api/v4/extract-results/batch/{}
 

+ 2 - 1
src/app/sample/models/metadata.py

@@ -14,7 +14,8 @@ class SampleMetadata(Base):
     knowledge_base_id = Column(String(36), nullable=False, comment="知识库ID")
     field_zh_name = Column(String(255), nullable=False, comment="字段名称(中文)")
     field_en_name = Column(String(500), nullable=False, comment="字段英文名称")
-    field_type = Column(String(10), nullable=False, comment="字段类型: text(文本), num(数字)")
+    field_type = Column(String(10), nullable=False, comment="字段类型: text(文本), num(数字), dict(字典)")
+    category_id = Column(String(50), nullable=True, comment="字典类型id")
     remark = Column(String(1000), nullable=True, comment="备注")
     
     def to_dict(self) -> dict:

+ 2 - 1
src/app/sample/schemas/knowledge_base.py

@@ -6,7 +6,8 @@ class MetadataField(BaseModel):
     """元数据字段定义"""
     field_zh_name: str = Field(..., description="中文名称")
     field_en_name: str = Field(..., description="英文名称")
-    field_type: str = Field(..., description="字段类型: text/num")
+    field_type: str = Field(..., description="字段类型: text/num/dict")
+    category_id: Optional[str] = Field(None, description="字典类型id(当field_type为dict时必填)")
     remark: Optional[str] = Field(None, description="备注")
 
 class KnowledgeBaseBase(BaseModel):

+ 4 - 0
src/app/sample/schemas/sample_schemas.py

@@ -19,6 +19,10 @@ class BatchDeleteRequest(BaseModel):
     ids: list[Union[int, str]]
     table_type: Optional[str] = None
 
+class BatchClearRequest(BaseModel):
+    ids: list[Union[int, str]]
+    table_type: Optional[str] = None
+
 class ConvertRequest(BaseModel):
     id: Union[int, str]
     table_type: Optional[str] = None

+ 1 - 1
src/app/schemas/base.py

@@ -61,7 +61,7 @@ class PageQuery(BaseModel):
 
 
 class ApiResponse(BaseModel):
-    code: int
+    code: str  # 改为字符串类型,支持 "000000" 格式
     message: str
     data: Optional[Any] = None
     timestamp: str = Field(default_factory=lambda: datetime.now(timezone.utc).isoformat())

+ 4 - 0
src/app/server/app.py

@@ -57,6 +57,8 @@ from views.snippet_view import router as snippet_router
 from views.tag_view import router as tag_router
 from views.search_engine_view import router as search_engine_router
 from views.image_view import router as image_router
+from views.dict_category_view import router as dict_category_router
+from views.dict_item_view import router as dict_item_router
 
 # 导入对外API路由
 from app.api.v1.system_api_view import router as system_api_router
@@ -317,6 +319,8 @@ app.include_router(snippet_router, prefix="/api/v1")
 app.include_router(tag_router, prefix="/api/v1")
 app.include_router(search_engine_router, prefix="/api/v1")
 app.include_router(image_router, prefix="/api/v1")
+app.include_router(dict_category_router, prefix="/api/v1")
+app.include_router(dict_item_router, prefix="/api/v1")
 
 
 

+ 308 - 0
src/app/services/dict_category_service.py

@@ -0,0 +1,308 @@
+"""
+字典类型服务层
+"""
+import sys
+import os
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../..'))
+
+import logging
+import uuid
+from typing import Optional, List, Dict, Any, Tuple
+from datetime import datetime
+from app.base.async_mysql_connection import get_db_connection
+
+logger = logging.getLogger(__name__)
+
+
+class DictCategoryService:
+    """字典类型服务类"""
+    
+    def __init__(self):
+        """初始化服务"""
+        pass
+    
+    async def get_category_tree(self) -> List[Dict[str, Any]]:
+        """获取字典类型树形结构"""
+        conn = get_db_connection()
+        if not conn:
+            return []
+        
+        cursor = conn.cursor()
+        
+        try:
+            cursor.execute("""
+                SELECT category_id, category_name, parent_field
+                FROM t_sys_dict_category
+                WHERE del_flag = '0'
+                ORDER BY category_id
+            """)
+            
+            categories = []
+            for row in cursor.fetchall():
+                category = {
+                    "category_id": row['category_id'],
+                    "category_name": row['category_name'],
+                    "parent_field": row['parent_field'],
+                    "children": []
+                }
+                categories.append(category)
+            
+            # 构建树形结构
+            tree = self._build_category_tree(categories)
+            return tree
+        except Exception as e:
+            logger.exception("获取字典类型树形结构错误")
+            return []
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def get_category_list(self, page: int, page_size: int, keyword: Optional[str] = None) -> Tuple[List[Dict[str, Any]], int]:
+        """获取字典类型列表"""
+        conn = get_db_connection()
+        if not conn:
+            return [], 0
+        
+        cursor = conn.cursor()
+        
+        try:
+            where_conditions = ["del_flag = '0'"]
+            params = []
+            
+            if keyword:
+                where_conditions.append("(category_name LIKE %s OR category_no LIKE %s)")
+                params.extend([f"%{keyword}%", f"%{keyword}%"])
+            
+            where_clause = " AND ".join(where_conditions)
+            
+            # 查询总数
+            cursor.execute(f"SELECT COUNT(*) as count FROM t_sys_dict_category WHERE {where_clause}", params)
+            total = cursor.fetchone()['count']
+            
+            # 查询列表
+            offset = (page - 1) * page_size
+            cursor.execute(f"""
+                SELECT dc.category_id, dc.category_no, dc.category_name, dc.category_desc,
+                       dc.parent_field, dc.category_level, dc.del_flag,
+                       dc.created_by, dc.created_time, dc.updated_by, dc.updated_time,
+                       creator.username as created_by_name,
+                       updater.username as updated_by_name
+                FROM t_sys_dict_category dc
+                LEFT JOIN t_sys_user creator ON dc.created_by = creator.id
+                LEFT JOIN t_sys_user updater ON dc.updated_by = updater.id
+                WHERE {where_clause}
+                ORDER BY dc.category_id
+                LIMIT %s OFFSET %s
+            """, params + [page_size, offset])
+            
+            categories = []
+            for row in cursor.fetchall():
+                category = {
+                    "category_id": row['category_id'],
+                    "category_no": row['category_no'],
+                    "category_name": row['category_name'],
+                    "category_desc": row['category_desc'],
+                    "parent_field": row['parent_field'],
+                    "category_level": row['category_level'],
+                    "del_flag": row['del_flag'],
+                    "created_by": row['created_by'],
+                    "created_time": row['created_time'].isoformat() if row['created_time'] else None,
+                    "updated_by": row['updated_by'],
+                    "updated_time": row['updated_time'].isoformat() if row['updated_time'] else None,
+                    "created_by_name": row['created_by_name'],
+                    "updated_by_name": row['updated_by_name']
+                }
+                categories.append(category)
+            
+            return categories, total
+        except Exception as e:
+            logger.exception("获取字典类型列表错误")
+            return [], 0
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def get_category_by_id(self, category_id: str) -> Optional[Dict[str, Any]]:
+        """根据ID获取字典类型"""
+        conn = get_db_connection()
+        if not conn:
+            return None
+        
+        cursor = conn.cursor()
+        
+        try:
+            cursor.execute("""
+                SELECT dc.category_id, dc.category_no, dc.category_name, dc.category_desc,
+                       dc.parent_field, dc.category_level, dc.del_flag,
+                       dc.created_by, dc.created_time, dc.updated_by, dc.updated_time,
+                       creator.username as created_by_name,
+                       updater.username as updated_by_name
+                FROM t_sys_dict_category dc
+                LEFT JOIN t_sys_user creator ON dc.created_by = creator.id
+                LEFT JOIN t_sys_user updater ON dc.updated_by = updater.id
+                WHERE dc.category_id = %s AND dc.del_flag = '0'
+            """, (category_id,))
+            
+            row = cursor.fetchone()
+            if not row:
+                return None
+            
+            category = {
+                "category_id": row['category_id'],
+                "category_no": row['category_no'],
+                "category_name": row['category_name'],
+                "category_desc": row['category_desc'],
+                "parent_field": row['parent_field'],
+                "category_level": row['category_level'],
+                "del_flag": row['del_flag'],
+                "created_by": row['created_by'],
+                "created_time": row['created_time'].isoformat() if row['created_time'] else None,
+                "updated_by": row['updated_by'],
+                "updated_time": row['updated_time'].isoformat() if row['updated_time'] else None,
+                "created_by_name": row['created_by_name'],
+                "updated_by_name": row['updated_by_name']
+            }
+            
+            return category
+        except Exception as e:
+            logger.exception("获取字典类型详情错误")
+            return None
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def create_category(self, category_data: Dict[str, Any], creator_id: str) -> Tuple[bool, str, Optional[str]]:
+        """创建字典类型"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败", None
+        
+        cursor = conn.cursor()
+        
+        try:
+            # 检查字典类型ID是否已存在
+            category_id = category_data.get('category_id') or str(uuid.uuid4())
+            cursor.execute("SELECT category_id FROM t_sys_dict_category WHERE category_id = %s", (category_id,))
+            if cursor.fetchone():
+                return False, "字典类型ID已存在", None
+            
+            # 创建字典类型
+            cursor.execute("""
+                INSERT INTO t_sys_dict_category 
+                (category_id, category_no, category_name, category_desc, parent_field, 
+                 category_level, del_flag, created_by, created_time, updated_time)
+                VALUES (%s, %s, %s, %s, %s, %s, '0', %s, NOW(), NOW())
+            """, (
+                category_id,
+                category_data.get('category_no'),
+                category_data['category_name'],
+                category_data.get('category_desc'),
+                category_data.get('parent_field', '0'),
+                category_data.get('category_level'),
+                creator_id
+            ))
+            
+            conn.commit()
+            return True, "字典类型创建成功", category_id
+        except Exception as e:
+            logger.exception("创建字典类型错误")
+            conn.rollback()
+            return False, "服务器内部错误", None
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def update_category(self, category_id: str, category_data: Dict[str, Any], updater_id: str) -> Tuple[bool, str]:
+        """更新字典类型"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败"
+        
+        cursor = conn.cursor()
+        
+        try:
+            # 检查字典类型是否存在
+            cursor.execute("SELECT category_id FROM t_sys_dict_category WHERE category_id = %s AND del_flag = '0'", (category_id,))
+            if not cursor.fetchone():
+                return False, "字典类型不存在"
+            
+            # 更新字典类型
+            update_fields = []
+            update_values = []
+            
+            for field in ['category_no', 'category_name', 'category_desc', 'parent_field', 'category_level']:
+                if field in category_data and category_data[field] is not None:
+                    update_fields.append(f'{field} = %s')
+                    update_values.append(category_data[field])
+            
+            if update_fields:
+                update_values.extend([updater_id, category_id])
+                cursor.execute(f"""
+                    UPDATE t_sys_dict_category 
+                    SET {', '.join(update_fields)}, updated_by = %s, updated_time = NOW()
+                    WHERE category_id = %s
+                """, update_values)
+            
+            conn.commit()
+            return True, "字典类型更新成功"
+        except Exception as e:
+            logger.exception("更新字典类型错误")
+            conn.rollback()
+            return False, "服务器内部错误"
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def delete_category(self, category_id: str) -> Tuple[bool, str]:
+        """删除字典类型(逻辑删除)"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败"
+        
+        cursor = conn.cursor()
+        
+        try:
+            # 检查字典类型是否存在
+            cursor.execute("SELECT category_id FROM t_sys_dict_category WHERE category_id = %s AND del_flag = '0'", (category_id,))
+            if not cursor.fetchone():
+                return False, "字典类型不存在"
+            
+            # 检查是否有子节点
+            cursor.execute("SELECT COUNT(*) as count FROM t_sys_dict_category WHERE parent_field = %s AND del_flag = '0'", (category_id,))
+            if cursor.fetchone()['count'] > 0:
+                return False, "该字典类型存在子节点,无法删除"
+            
+            # 检查是否有字典项
+            cursor.execute("SELECT COUNT(*) as count FROM t_sys_dict_category_item WHERE category_id = %s AND del_flag = '0'", (category_id,))
+            if cursor.fetchone()['count'] > 0:
+                return False, "该字典类型存在字典项,无法删除"
+            
+            # 逻辑删除
+            cursor.execute("""
+                UPDATE t_sys_dict_category 
+                SET del_flag = '1', updated_time = NOW()
+                WHERE category_id = %s
+            """, (category_id,))
+            
+            conn.commit()
+            return True, "字典类型删除成功"
+        except Exception as e:
+            logger.exception("删除字典类型错误")
+            conn.rollback()
+            return False, "服务器内部错误"
+        finally:
+            cursor.close()
+            conn.close()
+    
+    def _build_category_tree(self, categories: List[Dict[str, Any]], parent_id: str = "0") -> List[Dict[str, Any]]:
+        """构建字典类型树形结构"""
+        tree = []
+        for category in categories:
+            if category['parent_field'] == parent_id:
+                children = self._build_category_tree(categories, category['category_id'])
+                if children:
+                    category['children'] = children
+                tree.append(category)
+        return tree

+ 348 - 0
src/app/services/dict_item_service.py

@@ -0,0 +1,348 @@
+"""
+字典项服务层
+"""
+import sys
+import os
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../../..'))
+
+import logging
+from typing import Optional, List, Dict, Any, Tuple
+from datetime import datetime
+from app.base.async_mysql_connection import get_db_connection
+
+logger = logging.getLogger(__name__)
+
+
+class DictItemService:
+    """字典项服务类"""
+    
+    def __init__(self):
+        """初始化服务"""
+        pass
+    
+    async def get_item_list(
+        self, 
+        page: int, 
+        page_size: int, 
+        category_id: Optional[str] = None,
+        keyword: Optional[str] = None,
+        enable_flag: Optional[str] = None
+    ) -> Tuple[List[Dict[str, Any]], int]:
+        """获取字典项列表"""
+        conn = get_db_connection()
+        if not conn:
+            return [], 0
+        
+        cursor = conn.cursor()
+        
+        try:
+            where_conditions = ["di.del_flag = '0'"]
+            params = []
+            
+            if category_id:
+                where_conditions.append("di.category_id = %s")
+                params.append(category_id)
+            
+            if keyword:
+                where_conditions.append("(di.dict_name LIKE %s OR di.dict_value LIKE %s OR di.dict_desc LIKE %s)")
+                params.extend([f"%{keyword}%", f"%{keyword}%", f"%{keyword}%"])
+            
+            if enable_flag:
+                where_conditions.append("di.enable_flag = %s")
+                params.append(enable_flag)
+            
+            where_clause = " AND ".join(where_conditions)
+            
+            # 查询总数
+            cursor.execute(f"SELECT COUNT(*) as count FROM t_sys_dict_category_item di WHERE {where_clause}", params)
+            total = cursor.fetchone()['count']
+            
+            # 查询列表
+            offset = (page - 1) * page_size
+            cursor.execute(f"""
+                SELECT di.dict_id, di.dict_name, di.dict_value, di.dict_desc,
+                       di.category_id, di.enable_flag, di.del_flag, di.sort,
+                       di.created_by, di.created_time, di.updated_by, di.updated_time,
+                       dc.category_name,
+                       creator.username as created_by_name,
+                       updater.username as updated_by_name
+                FROM t_sys_dict_category_item di
+                LEFT JOIN t_sys_dict_category dc ON di.category_id = dc.category_id
+                LEFT JOIN t_sys_user creator ON di.created_by = creator.id
+                LEFT JOIN t_sys_user updater ON di.updated_by = updater.id
+                WHERE {where_clause}
+                ORDER BY di.sort ASC, di.dict_id ASC
+                LIMIT %s OFFSET %s
+            """, params + [page_size, offset])
+            
+            items = []
+            for row in cursor.fetchall():
+                item = {
+                    "dict_id": row['dict_id'],
+                    "dict_name": row['dict_name'],
+                    "dict_value": row['dict_value'],
+                    "dict_desc": row['dict_desc'],
+                    "category_id": row['category_id'],
+                    "category_name": row['category_name'],
+                    "enable_flag": row['enable_flag'],
+                    "del_flag": row['del_flag'],
+                    "sort": row['sort'],
+                    "created_by": row['created_by'],
+                    "created_time": row['created_time'].isoformat() if row['created_time'] else None,
+                    "updated_by": row['updated_by'],
+                    "updated_time": row['updated_time'].isoformat() if row['updated_time'] else None,
+                    "created_by_name": row['created_by_name'],
+                    "updated_by_name": row['updated_by_name']
+                }
+                items.append(item)
+            
+            return items, total
+        except Exception as e:
+            logger.exception("获取字典项列表错误")
+            return [], 0
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def get_item_by_id(self, dict_id: int) -> Optional[Dict[str, Any]]:
+        """根据ID获取字典项"""
+        conn = get_db_connection()
+        if not conn:
+            return None
+        
+        cursor = conn.cursor()
+        
+        try:
+            cursor.execute("""
+                SELECT di.dict_id, di.dict_name, di.dict_value, di.dict_desc,
+                       di.category_id, di.enable_flag, di.del_flag, di.sort,
+                       di.created_by, di.created_time, di.updated_by, di.updated_time,
+                       dc.category_name,
+                       creator.username as created_by_name,
+                       updater.username as updated_by_name
+                FROM t_sys_dict_category_item di
+                LEFT JOIN t_sys_dict_category dc ON di.category_id = dc.category_id
+                LEFT JOIN t_sys_user creator ON di.created_by = creator.id
+                LEFT JOIN t_sys_user updater ON di.updated_by = updater.id
+                WHERE di.dict_id = %s AND di.del_flag = '0'
+            """, (dict_id,))
+            
+            row = cursor.fetchone()
+            if not row:
+                return None
+            
+            item = {
+                "dict_id": row['dict_id'],
+                "dict_name": row['dict_name'],
+                "dict_value": row['dict_value'],
+                "dict_desc": row['dict_desc'],
+                "category_id": row['category_id'],
+                "category_name": row['category_name'],
+                "enable_flag": row['enable_flag'],
+                "del_flag": row['del_flag'],
+                "sort": row['sort'],
+                "created_by": row['created_by'],
+                "created_time": row['created_time'].isoformat() if row['created_time'] else None,
+                "updated_by": row['updated_by'],
+                "updated_time": row['updated_time'].isoformat() if row['updated_time'] else None,
+                "created_by_name": row['created_by_name'],
+                "updated_by_name": row['updated_by_name']
+            }
+            
+            return item
+        except Exception as e:
+            logger.exception("获取字典项详情错误")
+            return None
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def create_item(self, item_data: Dict[str, Any], creator_id: str) -> Tuple[bool, str, Optional[int]]:
+        """创建字典项"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败", None
+        
+        cursor = conn.cursor()
+        
+        try:
+            # 检查字典类型是否存在
+            cursor.execute("SELECT category_id FROM t_sys_dict_category WHERE category_id = %s AND del_flag = '0'", 
+                         (item_data['category_id'],))
+            if not cursor.fetchone():
+                return False, "字典类型不存在", None
+            
+            # 创建字典项
+            cursor.execute("""
+                INSERT INTO t_sys_dict_category_item 
+                (dict_name, dict_value, dict_desc, category_id, enable_flag, del_flag, sort, 
+                 created_by, created_time, updated_time)
+                VALUES (%s, %s, %s, %s, %s, '0', %s, %s, NOW(), NOW())
+            """, (
+                item_data['dict_name'],
+                item_data['dict_value'],
+                item_data.get('dict_desc'),
+                item_data['category_id'],
+                item_data.get('enable_flag', '1'),
+                item_data.get('sort'),
+                creator_id
+            ))
+            
+            dict_id = cursor.lastrowid
+            conn.commit()
+            return True, "字典项创建成功", dict_id
+        except Exception as e:
+            logger.exception("创建字典项错误")
+            conn.rollback()
+            return False, "服务器内部错误", None
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def update_item(self, dict_id: int, item_data: Dict[str, Any], updater_id: str) -> Tuple[bool, str]:
+        """更新字典项"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败"
+        
+        cursor = conn.cursor()
+        
+        try:
+            # 检查字典项是否存在
+            cursor.execute("SELECT dict_id FROM t_sys_dict_category_item WHERE dict_id = %s AND del_flag = '0'", (dict_id,))
+            if not cursor.fetchone():
+                return False, "字典项不存在"
+            
+            # 如果更新了category_id,检查字典类型是否存在
+            if 'category_id' in item_data and item_data['category_id']:
+                cursor.execute("SELECT category_id FROM t_sys_dict_category WHERE category_id = %s AND del_flag = '0'", 
+                             (item_data['category_id'],))
+                if not cursor.fetchone():
+                    return False, "字典类型不存在"
+            
+            # 更新字典项
+            update_fields = []
+            update_values = []
+            
+            for field in ['dict_name', 'dict_value', 'dict_desc', 'category_id', 'enable_flag', 'sort']:
+                if field in item_data and item_data[field] is not None:
+                    update_fields.append(f'{field} = %s')
+                    update_values.append(item_data[field])
+            
+            if update_fields:
+                update_values.extend([updater_id, dict_id])
+                cursor.execute(f"""
+                    UPDATE t_sys_dict_category_item 
+                    SET {', '.join(update_fields)}, updated_by = %s, updated_time = NOW()
+                    WHERE dict_id = %s
+                """, update_values)
+            
+            conn.commit()
+            return True, "字典项更新成功"
+        except Exception as e:
+            logger.exception("更新字典项错误")
+            conn.rollback()
+            return False, "服务器内部错误"
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def delete_item(self, dict_id: int) -> Tuple[bool, str]:
+        """删除字典项(逻辑删除)"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败"
+        
+        cursor = conn.cursor()
+        
+        try:
+            # 检查字典项是否存在
+            cursor.execute("SELECT dict_id FROM t_sys_dict_category_item WHERE dict_id = %s AND del_flag = '0'", (dict_id,))
+            if not cursor.fetchone():
+                return False, "字典项不存在"
+            
+            # 逻辑删除
+            cursor.execute("""
+                UPDATE t_sys_dict_category_item 
+                SET del_flag = '1', updated_time = NOW()
+                WHERE dict_id = %s
+            """, (dict_id,))
+            
+            conn.commit()
+            return True, "字典项删除成功"
+        except Exception as e:
+            logger.exception("删除字典项错误")
+            conn.rollback()
+            return False, "服务器内部错误"
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def batch_delete_items(self, dict_ids: List[int]) -> Tuple[bool, str]:
+        """批量删除字典项(逻辑删除)"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败"
+        
+        cursor = conn.cursor()
+        
+        try:
+            if not dict_ids:
+                return False, "字典项ID列表不能为空"
+            
+            # 批量逻辑删除
+            placeholders = ','.join(['%s'] * len(dict_ids))
+            cursor.execute(f"""
+                UPDATE t_sys_dict_category_item 
+                SET del_flag = '1', updated_time = NOW()
+                WHERE dict_id IN ({placeholders}) AND del_flag = '0'
+            """, dict_ids)
+            
+            affected_rows = cursor.rowcount
+            conn.commit()
+            
+            if affected_rows == 0:
+                return False, "没有找到可删除的字典项"
+            
+            return True, f"成功删除{affected_rows}个字典项"
+        except Exception as e:
+            logger.exception("批量删除字典项错误")
+            conn.rollback()
+            return False, "服务器内部错误"
+        finally:
+            cursor.close()
+            conn.close()
+    
+    async def toggle_item_status(self, dict_id: int, enable_flag: str, updater_id: str) -> Tuple[bool, str]:
+        """切换字典项启用状态"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败"
+        
+        cursor = conn.cursor()
+        
+        try:
+            # 检查字典项是否存在
+            cursor.execute("SELECT dict_id FROM t_sys_dict_category_item WHERE dict_id = %s AND del_flag = '0'", (dict_id,))
+            if not cursor.fetchone():
+                return False, "字典项不存在"
+            
+            # 更新状态
+            cursor.execute("""
+                UPDATE t_sys_dict_category_item 
+                SET enable_flag = %s, updated_by = %s, updated_time = NOW()
+                WHERE dict_id = %s
+            """, (enable_flag, updater_id, dict_id))
+            
+            conn.commit()
+            status_text = "启用" if enable_flag == "1" else "禁用"
+            return True, f"字典项{status_text}成功"
+        except Exception as e:
+            logger.exception("切换字典项状态错误")
+            conn.rollback()
+            return False, "服务器内部错误"
+        finally:
+            cursor.close()
+            conn.close()

+ 107 - 8
src/app/services/image_service.py

@@ -324,14 +324,8 @@ class ImageService:
             if not image_ids:
                 return False, "未指定要加入任务的图片 ID"
             
-            # 0. 获取或生成项目 UUID
-            project_id = None
-            cursor.execute("SELECT project_id FROM t_task_management WHERE project_name = %s LIMIT 1", (project_name,))
-            existing_project = cursor.fetchone()
-            if existing_project:
-                project_id = existing_project['project_id']
-            else:
-                project_id = str(uuid.uuid4())
+            # 0. 每次点击都生成一个新的项目 UUID (不再检查重名复用)
+            project_id = str(uuid.uuid4())
             
             # 处理标签
             tag_str = json.dumps(tags, ensure_ascii=False) if tags else None
@@ -402,6 +396,111 @@ class ImageService:
             if conn:
                 conn.close()
 
+    async def category_batch_check(self, category_id: str) -> Tuple[bool, str, Dict[str, Any]]:
+        """按分类批量推送前的预检查 (获取图片总数和子分类列表)"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败", {}
+        
+        cursor = conn.cursor()
+        try:
+            # 1. 获取所有涉及的分类 ID
+            target_category_ids = []
+            if not category_id or category_id == '0':
+                cursor.execute("SELECT id FROM t_image_category")
+                target_category_ids = [str(row['id']) for row in cursor.fetchall()]
+            else:
+                target_category_ids = [category_id]
+                child_ids = await self._get_all_child_category_ids(category_id)
+                target_category_ids.extend(child_ids)
+            
+            if not target_category_ids:
+                return False, "分类不存在", {"image_count": 0}
+
+            # 2. 获取这些分类下的所有图片总数
+            placeholders = ', '.join(['%s'] * len(target_category_ids))
+            sql = f"SELECT COUNT(*) as count FROM t_image_info WHERE image_type IN ({placeholders})"
+            cursor.execute(sql, tuple(target_category_ids))
+            image_count = cursor.fetchone()['count']
+            
+            # 3. 获取涉及的子分类名称
+            sub_category_names = []
+            if len(target_category_ids) > 1:
+                sub_ids = target_category_ids[1:] if category_id and category_id != '0' else target_category_ids
+                sub_placeholders = ', '.join(['%s'] * len(sub_ids))
+                cursor.execute(f"SELECT type_name FROM t_image_category WHERE id IN ({sub_placeholders})", tuple(sub_ids))
+                sub_category_names = [row['type_name'] for row in cursor.fetchall()]
+
+            return True, "查询成功", {
+                "image_count": image_count,
+                "sub_categories": sub_category_names
+            }
+        except Exception as e:
+            logger.exception("按分类批量检查失败")
+            return False, f"操作失败: {str(e)}", {}
+        finally:
+            if cursor:
+                cursor.close()
+            if conn:
+                conn.close()
+
+    async def category_batch_add_to_task(self, category_id: str, username: str, project_name: str, tags: List[str] = None) -> Tuple[bool, str, Dict[str, Any]]:
+        """按分类批量将图片加入任务中心 (支持递归)"""
+        conn = get_db_connection()
+        if not conn:
+            return False, "数据库连接失败", {}
+        
+        cursor = conn.cursor()
+        try:
+            # 1. 获取所有涉及的分类 ID
+            target_category_ids = []
+            if not category_id or category_id == '0':
+                # 如果没选分类,默认获取所有
+                cursor.execute("SELECT id FROM t_image_category")
+                target_category_ids = [str(row['id']) for row in cursor.fetchall()]
+            else:
+                target_category_ids = [category_id]
+                child_ids = await self._get_all_child_category_ids(category_id)
+                target_category_ids.extend(child_ids)
+            
+            if not target_category_ids:
+                return False, "分类不存在", {"image_count": 0}
+
+            # 2. 获取这些分类下的所有图片 ID
+            placeholders = ', '.join(['%s'] * len(target_category_ids))
+            sql = f"SELECT id FROM t_image_info WHERE image_type IN ({placeholders})"
+            cursor.execute(sql, tuple(target_category_ids))
+            image_rows = cursor.fetchall()
+            image_ids = [str(row['id']) for row in image_rows]
+            
+            if not image_ids:
+                return True, "该分类下无图片,无法推送", {"image_count": 0}
+
+            # 3. 获取涉及的子分类名称 (用于前端展示)
+            sub_category_names = []
+            if len(target_category_ids) > 1:
+                # 排除当前分类本身,只取子分类名称
+                sub_ids = target_category_ids[1:] if category_id and category_id != '0' else target_category_ids
+                sub_placeholders = ', '.join(['%s'] * len(sub_ids))
+                cursor.execute(f"SELECT type_name FROM t_image_category WHERE id IN ({sub_placeholders})", tuple(sub_ids))
+                sub_category_names = [row['type_name'] for row in cursor.fetchall()]
+
+            # 4. 调用现有的批量加入逻辑
+            success, message = await self.batch_add_to_task(image_ids, username, project_name, tags)
+            
+            return success, message, {
+                "image_count": len(image_ids),
+                "sub_categories": sub_category_names
+            }
+        except Exception as e:
+            logger.exception("按分类批量加入任务失败")
+            return False, f"操作失败: {str(e)}", {}
+        finally:
+            if cursor:
+                cursor.close()
+            if conn:
+                conn.close()
+
     async def get_upload_url(self, filename: str, content_type: str, prefix: str = None) -> Tuple[bool, str, Dict[str, Any]]:
         """获取 MinIO 预签名上传 URL"""
         try:

+ 42 - 15
src/app/services/knowledge_base_service.py

@@ -45,20 +45,15 @@ class KnowledgeBaseService:
                 milvus_service.set_collection_state(collection_name, "load")
             
             # 尝试使用 count(*) 获取准确的实时数量
-            # 过滤掉已标记删除的数据 (is_deleted == false)
+            # 过滤掉已标记删除的数据 (is_deleted == 0)
             try:
-                # [Fix] 动态检测 is_deleted 类型,自适应 Int64 或 Bool
+                # [Fix] is_deleted 现在统一使用 INT8 类型 (0=未删除, 1=已删除)
                 desc = milvus_service.client.describe_collection(collection_name)
                 fields = desc.get('fields', [])
                 is_del_field = next((f for f in fields if f['name'] == 'is_deleted'), None)
                 
-                filter_expr = ""
-                if is_del_field:
-                     if is_del_field.get('type') == 1: # Bool=1
-                         filter_expr = "is_deleted == false"
-                     else:
-                         filter_expr = "is_deleted == 0" # 默认 Int
-                else:
+                filter_expr = "is_deleted == false"
+                if not is_del_field:
                     # 字段不存在,使用恒真条件
                     pk_field = "id"
                     for f in fields:
@@ -375,6 +370,7 @@ class KnowledgeBaseService:
                         field_zh_name=field.field_zh_name,
                         field_en_name=field.field_en_name,
                         field_type=field.field_type,
+                        category_id=field.category_id if field.field_type == 'dict' else None,
                         remark=field.remark
                     )
                     db.add(new_metadata)
@@ -434,6 +430,7 @@ class KnowledgeBaseService:
                         field_zh_name=field.field_zh_name,
                         field_en_name=field.field_en_name,
                         field_type=field.field_type,
+                        category_id=field.category_id if field.field_type == 'dict' else None,
                         remark=field.remark
                     )
                     db.add(new_metadata)
@@ -560,7 +557,7 @@ class KnowledgeBaseService:
             {"name": "tag_list", "type": "VARCHAR", "max_length": 2048, "description": "标签"},
             {"name": "permission", "type": "JSON", "description": "权限"},
             {"name": "metadata", "type": "JSON", "description": "元数据"},
-            {"name": "is_deleted", "type": "BOOL", "description": "删除标志"},
+            {"name": "is_deleted", "type": "BOOL", "description": "删除标志(false=未删除,true=已删除)"},
             {"name": "created_by", "type": "VARCHAR", "max_length": 128, "description": "创建人"},
             {"name": "created_time", "type": "INT64", "description": "创建时间"},
             {"name": "updated_by", "type": "VARCHAR", "max_length": 128, "description": "修改人"},
@@ -611,10 +608,31 @@ class KnowledgeBaseService:
         if not kb:
             raise ValueError("知识库不存在")
 
-        # 查询元数据表
-        meta_query = select(SampleMetadata).where(SampleMetadata.knowledge_base_id == kb_id)
-        meta_result = await db.execute(meta_query)
-        metadata_fields = [f.to_dict() for f in meta_result.scalars().all()]
+        # 查询元数据表,关联字典类别表获取类别名称
+        from sqlalchemy import text
+        query_sql = text("""
+            SELECT 
+                m.id, m.knowledge_base_id, m.field_zh_name, m.field_en_name, 
+                m.field_type, m.category_id, m.remark,
+                dc.category_name
+            FROM t_samp_metadata m
+            LEFT JOIN t_sys_dict_category dc ON m.category_id = dc.category_id AND dc.del_flag = '0'
+            WHERE m.knowledge_base_id = :kb_id
+        """)
+        meta_result = await db.execute(query_sql, {"kb_id": kb_id})
+        metadata_fields = []
+        for row in meta_result:
+            field_dict = {
+                "id": row.id,
+                "knowledge_base_id": row.knowledge_base_id,
+                "field_zh_name": row.field_zh_name,
+                "field_en_name": row.field_en_name,
+                "field_type": row.field_type,
+                "category_id": row.category_id,
+                "category_name": row.category_name,
+                "remark": row.remark
+            }
+            metadata_fields.append(field_dict)
         
         # 自动推断逻辑:如果 DB 中没有定义元数据,且 Milvus 中有数据,尝试推断 (优先推断 collection_name_parent)
         target_col = kb.collection_name_parent
@@ -669,7 +687,16 @@ class KnowledgeBaseService:
                                 remark="Auto inferred from Milvus data"
                             )
                             db.add(new_metadata)
-                            new_fields.append(new_metadata.to_dict())
+                            new_fields.append({
+                                "id": new_metadata.id,
+                                "knowledge_base_id": new_metadata.knowledge_base_id,
+                                "field_zh_name": new_metadata.field_zh_name,
+                                "field_en_name": new_metadata.field_en_name,
+                                "field_type": new_metadata.field_type,
+                                "category_id": None,
+                                "category_name": None,
+                                "remark": new_metadata.remark
+                            })
                         
                         await db.commit()
                         metadata_fields = new_fields

+ 19 - 12
src/app/services/milvus_service.py

@@ -16,6 +16,7 @@ from datetime import datetime
 from app.base import get_milvus_manager, get_milvus_vectorstore, get_embedding_model
 from app.core.config import config_handler
 from langchain_core.documents import Document
+from pymilvus import DataType, Function, FunctionType
 
 logger = logging.getLogger(__name__)
 
@@ -77,7 +78,8 @@ class MilvusService:
                 
                 parent_docs = []
                 child_docs = []
-                global_idx = 0
+                parent_global_idx = 0
+                child_global_idx = 0
                 
                 for split in md_header_splits:
                     # 获取预处理后的层级路径
@@ -86,21 +88,21 @@ class MilvusService:
                     # 对每个标题块进行父段切分
                     split_parent_chunks = parent_splitter.split_text(split.page_content)
                     
-                    for p_idx, p_content in enumerate(split_parent_chunks):
-                        p_id = hashlib.sha1(f"{doc_id}_p_{global_idx}_{p_idx}".encode()).hexdigest()
-                        p_metadata = self._prepare_metadata(doc_info, p_id, global_idx, p_id, hierarchy)
+                    for p_content in split_parent_chunks:
+                        p_id = hashlib.sha1(f"{doc_id}_p_{parent_global_idx}".encode()).hexdigest()
+                        p_metadata = self._prepare_metadata(doc_info, p_id, parent_global_idx, p_id, hierarchy)
                         parent_docs.append(Document(page_content=p_content, metadata=p_metadata))
+                        parent_global_idx += 1
                         
                         # 2. 在每个父段内部切分子段 (较小块)
                         child_splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=30)
                         child_chunks = child_splitter.split_text(p_content)
                         
-                        for c_idx, c_content in enumerate(child_chunks):
-                            c_id = hashlib.sha1(f"{doc_id}_c_{p_id}_{c_idx}".encode()).hexdigest()
-                            c_metadata = self._prepare_metadata(doc_info, c_id, c_idx, p_id, hierarchy)
+                        for c_content in child_chunks:
+                            c_id = hashlib.sha1(f"{doc_id}_c_{child_global_idx}".encode()).hexdigest()
+                            c_metadata = self._prepare_metadata(doc_info, c_id, child_global_idx, p_id, hierarchy)
                             child_docs.append(Document(page_content=c_content, metadata=c_metadata))
-                        
-                        global_idx += 1
+                            child_global_idx += 1
 
                 # 确保两个集合都存在
                 self.ensure_collection_exists(parent_col)
@@ -185,7 +187,6 @@ class MilvusService:
                 "issuing_authority": business_meta.get("issuing_authority") or "无",
                 "document_type": business_meta.get("document_type") or "未分类",
                 "professional_field": business_meta.get("professional_field") or "未分类",
-                "validity": business_meta.get("validity") or "现行",
                 "file_url": business_meta.get("file_url") or ""
             })
         elif source_type == 'construction_plan':
@@ -223,7 +224,7 @@ class MilvusService:
 
     def ensure_collection_exists(self, name: str):
         """确保指定名称的集合存在,不存在则按默认 Schema 创建"""
-        from pymilvus import DataType, Function, FunctionType
+        
         
         # 1. 如果不存在,则创建集合
         if not self.client.has_collection(name):
@@ -239,7 +240,7 @@ class MilvusService:
             schema.add_field("tag_list", DataType.VARCHAR, max_length=2048)
             schema.add_field("permission", DataType.JSON, nullable=True)
             schema.add_field("metadata", DataType.JSON, nullable=True)
-            schema.add_field("is_deleted", DataType.INT64, default_value=0)
+            schema.add_field("is_deleted", DataType.BOOL, default_value=0)
             schema.add_field("created_by", DataType.VARCHAR, max_length=256, nullable=True)
             schema.add_field("created_time", DataType.INT64)
             schema.add_field("updated_by", DataType.VARCHAR, max_length=256, nullable=True)
@@ -399,6 +400,12 @@ class MilvusService:
                 if dtype == DataType.FLOAT_VECTOR:
                     kwargs["dim"] = dimension # 使用传入的 dimension
                 
+                # [FIX] 对于 BOOL 类型,Milvus 某些版本可能不支持或有问题
+                # 如果是 is_deleted 字段且类型为 BOOL,改用 INT8 (0/1) 代替
+                if f.get("name") == "is_deleted" and dtype == DataType.BOOL:
+                    #logger.warning("Converting is_deleted from BOOL to INT8 for compatibility")
+                    kwargs["datatype"] = DataType.BOOL
+                
                 schema.add_field(**kwargs)
 
                 # 如果是 BM25 类型,记录下来以便后续添加 Function

Разница между файлами не показана из-за своего большого размера
+ 663 - 124
src/app/services/sample_service.py


+ 3 - 3
src/app/services/snippet_service.py

@@ -95,9 +95,9 @@ class SnippetService:
                 # 构建过滤表达式
                 filter_exprs = []
                 
-                # 状态过滤 (假设 is_deleted=False 表示 normal, True 表示 disabled/deleted)
+                # 状态过滤 (假设 is_deleted=0 表示 normal, 1 表示 disabled/deleted)
                 # 注意:Milvus 中通常使用 is_deleted 标记删除,这里我们需要根据传入的 status 映射
-                # status: 'normal' -> is_deleted == False
+                # status: 'normal' -> is_deleted == 0
                 # status: 'disabled' -> 实际上我们没有 disabled 状态,只有删除。如果必须支持,可能需要额外的 status 字段
                 # 暂时假设:normal = 未删除, disabled = (无此状态或暂不支持)
                 
@@ -118,7 +118,7 @@ class SnippetService:
                     elif status == 'disabled':
                         # 假设 disabled 对应 is_deleted == true (软删除状态)
                         # 但通常 deleted 数据不应被查出。如果需要查出“已禁用”,则需要额外字段。
-                        # 按照之前 Schema 定义,is_deleted 是 bool
+                        # 按照之前 Schema 定义,is_deleted 是 INT8 (0/1)
                         filter_exprs.append("is_deleted == true")
                     else:
                         # 默认只查未删除的

+ 100 - 69
src/app/services/task_service.py

@@ -2,7 +2,7 @@
 import logging
 import json
 import httpx
-from datetime import datetime
+from datetime import datetime, date
 from typing import List, Dict, Any, Tuple, Optional
 from app.base.async_mysql_connection import get_db_connection
 from app.base.minio_connection import get_minio_manager
@@ -66,6 +66,11 @@ class TaskService:
                 cursor.execute("CREATE UNIQUE INDEX uk_business_project ON t_task_management (business_id, project_id)")
                 logger.info("Created new composite unique index: uk_business_project")
             
+            cursor.execute("SHOW INDEX FROM t_task_management WHERE Key_name = 'idx_project_id'")
+            if not cursor.fetchone():
+                cursor.execute("CREATE INDEX idx_project_id ON t_task_management (project_id)")
+                logger.info("Created index: idx_project_id")
+            
             conn.commit()
             TaskService._schema_verified = True
         except Exception as e:
@@ -89,6 +94,7 @@ class TaskService:
                 VALUES (%s, %s, %s, %s, %s, %s, %s, %s)
                 ON DUPLICATE KEY UPDATE 
                     task_id = IFNULL(VALUES(task_id), task_id),
+                    project_id = IFNULL(VALUES(project_id), project_id),
                     project_name = IFNULL(VALUES(project_name), project_name),
                     annotation_status = IFNULL(VALUES(annotation_status), annotation_status),
                     tag = IFNULL(VALUES(tag), tag),
@@ -133,8 +139,8 @@ class TaskService:
             return {k: self._serialize_datetime(v) for k, v in obj.items()}
         elif isinstance(obj, list):
             return [self._serialize_datetime(i) for i in obj]
-        elif isinstance(obj, datetime):
-            return obj.strftime('%Y-%m-%d %H:%M:%S')
+        elif isinstance(obj, (datetime, date)):
+            return obj.strftime('%Y-%m-%d %H:%M:%S') if isinstance(obj, datetime) else obj.strftime('%Y-%m-%d')
         return obj
 
     async def get_task_list(self, task_type: str) -> List[Dict[str, Any]]:
@@ -472,25 +478,45 @@ class TaskService:
             
             # 针对 'data' 类型的批量 Milvus 查询优化
             milvus_data_map = {}
+            missing_milvus_ids = []
             if internal_task_type == 'data':
                 all_task_ids = [r['id'] for r in rows]
                 sql_kb = """
                     SELECT kb.collection_name_parent, kb.collection_name_children
                     FROM t_samp_document_main d
                     LEFT JOIN t_samp_knowledge_base kb ON d.kb_id = kb.id
-                    WHERE d.id COLLATE utf8mb4_unicode_ci = %s COLLATE utf8mb4_unicode_ci
+                    WHERE d.id = %s
                 """
                 cursor.execute(sql_kb, (all_task_ids[0],))
                 kb_info = cursor.fetchone()
                 if kb_info:
                     milvus_data_map = self._get_milvus_content_batch(all_task_ids, kb_info)
+                
+                # 记录缺失 Milvus 数据的 ID
+                missing_milvus_ids = [tid for tid in all_task_ids if not milvus_data_map.get(tid)]
+            
+            # 针对缺失数据的批量标题查询
+            title_map = {}
+            if missing_milvus_ids:
+                placeholders = ', '.join(['%s'] * len(missing_milvus_ids))
+                cursor.execute(f"SELECT id, title FROM t_samp_document_main WHERE id IN ({placeholders})", missing_milvus_ids)
+                title_map = {r['id']: r['title'] for r in cursor.fetchall()}
+
+            # 针对 'image' 类型的批量 MinIO 查询优化
+            image_data_map = {}
+            if internal_task_type == 'image':
+                all_image_ids = [r['id'] for r in rows]
+                placeholders = ', '.join(['%s'] * len(all_image_ids))
+                cursor.execute(f"SELECT id, image_url FROM t_image_info WHERE id IN ({placeholders})", all_image_ids)
+                for img_row in cursor.fetchall():
+                    img_url = img_row['image_url']
+                    if img_url and not img_url.startswith('http'):
+                        img_url = self.minio_manager.get_full_url(img_url)
+                    image_data_map[img_row['id']] = [img_url] if img_url else []
 
             for item in rows:
                 task_id = item['id']
                 
-                # 记录原始数据状态
-                logger.debug(f"正在处理导出任务 {task_id}, tag: {item.get('tag')}, metadata_keys: {list(json.loads(item['metadata']).keys()) if item.get('metadata') else 'None'}")
-                
                 # 提取并处理标签
                 doc_tags = []
                 if item.get('tag'):
@@ -500,30 +526,24 @@ class TaskService:
                             for t in doc_tags: all_project_tags.add(t)
                     except: pass
                 
-                # 解析数据库元数据
+                # 解析数据库元数据 (提前序列化日期)
                 db_metadata = {}
                 if item.get('metadata'):
                     try:
                         db_metadata = json.loads(item['metadata']) if isinstance(item['metadata'], str) else item['metadata']
+                        if db_metadata:
+                            db_metadata = self._serialize_datetime(db_metadata)
                     except: pass
                 
                 # 获取任务内容
                 task_contents = []
-                annotation_results = []
                 if internal_task_type == 'data':
                     task_contents = milvus_data_map.get(task_id, [])
                     if not task_contents:
-                        cursor.execute("SELECT title FROM t_samp_document_main WHERE id = %s", (task_id,))
-                        res = cursor.fetchone()
-                        if res: task_contents = [res['title']]
+                        title = title_map.get(task_id)
+                        if title: task_contents = [title]
                 elif internal_task_type == 'image':
-                    cursor.execute("SELECT image_url FROM t_image_info WHERE id = %s", (task_id,))
-                    res = cursor.fetchone()
-                    if res:
-                        img_url = res['image_url']
-                        if img_url and not img_url.startswith('http'):
-                            img_url = self.minio_manager.get_full_url(img_url)
-                        task_contents = [img_url]
+                    task_contents = image_data_map.get(task_id, [])
 
                 # 构建最终任务列表
                 for idx, content in enumerate(task_contents):
@@ -552,15 +572,15 @@ class TaskService:
                     
                     final_tasks.append(task_item)
 
-            # 统一进行一次递归序列化处理
-            return self._serialize_datetime({
+            # 准备返回结果,不再进行全局递归序列化 (已在局部处理)
+            return {
                 "name": project_name,
                 "description": "",
                 "task_type": external_task_type,
                 "data": final_tasks,
                 "external_id": remote_project_id,
                 "tags": [{"tag": t} for t in sorted(list(all_project_tags))]
-            })
+            }
         except Exception as e:
             logger.exception(f"导出项目数据异常: {e}")
             return {}
@@ -603,25 +623,30 @@ class TaskService:
                 id_field = fields["id"]
                 content_field = fields["content"]
                 
-                # 使用 in 表达式进行批量查询
-                # 注意:如果 task_ids 非常多,可能需要分批(如每批 100 个)
-                id_list_str = ", ".join([f'"{tid}"' for tid in task_ids])
-                res = milvus_service.client.query(
-                    collection_name=coll_name,
-                    filter=f'{id_field} in [{id_list_str}]',
-                    output_fields=[id_field, content_field]
-                )
-                
-                if res:
-                    for s in res:
-                        tid = s.get(id_field)
-                        val = s.get(content_field)
-                        if tid in result_map and val:
-                            result_map[tid].append(val)
+                # 使用 in 表达式进行批量查询 (分批处理以防 ID 过多)
+                CHUNK_SIZE = 100
+                for i in range(0, len(task_ids), CHUNK_SIZE):
+                    chunk_ids = task_ids[i:i + CHUNK_SIZE]
+                    id_list_str = ", ".join([f'"{tid}"' for tid in chunk_ids])
                     
-                    # 如果当前集合已经查到了内容,就不再查兜底集合(逻辑同单条查询)
-                    if any(result_map.values()):
-                        return result_map
+                    logger.info(f"正在从 Milvus 集合 {coll_name} 查询分片内容 ({i}/{len(task_ids)})...")
+                    res = milvus_service.client.query(
+                        collection_name=coll_name,
+                        filter=f'{id_field} in [{id_list_str}]',
+                        output_fields=[id_field, content_field]
+                    )
+                    
+                    if res:
+                        for s in res:
+                            tid = s.get(id_field)
+                            val = s.get(content_field)
+                            if tid in result_map and val:
+                                result_map[tid].append(val)
+                
+                # 如果当前集合已经查到了内容,就不再查兜底集合 (除非结果仍为空)
+                if any(result_map.values()):
+                    logger.info(f"从 Milvus 集合 {coll_name} 查得 {sum(len(v) for v in result_map.values())} 条内容分片")
+                    return result_map
             except Exception as e:
                 logger.error(f"批量查询 Milvus 集合 {coll_name} 异常: {e}")
                 continue
@@ -853,12 +878,13 @@ class TaskService:
 
     async def send_to_external_platform(self, project_id: str) -> Tuple[bool, str]:
         """将项目数据推送至外部标注平台 (单表化)"""
+        # 1. 准备数据 (导出数据需要数据库连接)
         conn = get_db_connection()
         if not conn:
             return False, "数据库连接失败"
             
         try:
-            # 1. 准备数据 (复用数据库连接)
+            logger.info(f"开始导出项目 {project_id} 数据...")
             payload = await self.export_project_data(project_id=project_id, conn=conn)
             
             if not payload:
@@ -866,8 +892,15 @@ class TaskService:
                 
             if not payload.get('data'):
                 return False, f"项目数据为空 (查询到0条有效任务),无法推送"
+        except Exception as e:
+            logger.exception(f"导出项目数据异常: {e}")
+            return False, f"导出异常: {str(e)}"
+        finally:
+            # 及时释放连接,防止在 HTTP 请求期间占用
+            conn.close()
             
-            # 2. 获取配置
+        # 2. 获取配置
+        try:
             from app.core.config import config_handler
             api_base_url = config_handler.get('external_api', 'project_api_url', 'http://192.168.92.61:9003/api/external/projects').rstrip('/')
             api_url = f"{api_base_url}/init"
@@ -880,8 +913,8 @@ class TaskService:
             if not token:
                 return False, "外部平台Token未配置"
 
-            # 3. 发送请求
-            async with httpx.AsyncClient(timeout=60.0) as client:
+            # 3. 发送请求 (不持有数据库连接)
+            async with httpx.AsyncClient(timeout=120.0) as client: # 增加超时时间到 120s
                 headers = {
                     "Authorization": f"Bearer {token}",
                     "Content-Type": "application/json"
@@ -893,30 +926,30 @@ class TaskService:
                     res_data = response.json()
                     logger.info(f"外部平台推送成功响应: {res_data}")
                     remote_project_id = res_data.get('project_id')
-                    
-                    # 获取下载地址并回写 (兼容多种返回格式)
                     download_url = res_data.get('download_url') or res_data.get('file_url')
-                    if download_url:
-                        # 4. 回写外部项目 ID 和下载地址 (复用当前连接)
-                        cursor = conn.cursor()
-                        # 先检查受影响行数
-                        affected = cursor.execute(
-                            "UPDATE t_task_management SET task_id = %s, file_url = %s WHERE project_id = %s", 
-                            (remote_project_id, download_url, project_id)
-                        )
-                        conn.commit()
-                        cursor.close()
-                        logger.info(f"已回写 task_id: {remote_project_id} 和 file_url: {download_url}, 受影响行数: {affected}")
-                    elif remote_project_id:
-                        # 仅回写外部项目 ID
-                        cursor = conn.cursor()
-                        affected = cursor.execute(
-                            "UPDATE t_task_management SET task_id = %s WHERE project_id = %s", 
-                            (remote_project_id, project_id)
-                        )
-                        conn.commit()
-                        cursor.close()
-                        logger.info(f"仅回写 task_id: {remote_project_id}, 受影响行数: {affected}")
+                    
+                    # 4. 回写外部项目 ID 和下载地址 (重新获取连接)
+                    if remote_project_id:
+                        conn = get_db_connection()
+                        if conn:
+                            try:
+                                cursor = conn.cursor()
+                                if download_url:
+                                    affected = cursor.execute(
+                                        "UPDATE t_task_management SET task_id = %s, file_url = %s WHERE project_id = %s", 
+                                        (remote_project_id, download_url, project_id)
+                                    )
+                                    logger.info(f"已回写 task_id: {remote_project_id} 和 file_url: {download_url}, 受影响行数: {affected}")
+                                else:
+                                    affected = cursor.execute(
+                                        "UPDATE t_task_management SET task_id = %s WHERE project_id = %s", 
+                                        (remote_project_id, project_id)
+                                    )
+                                    logger.info(f"仅回写 task_id: {remote_project_id}, 受影响行数: {affected}")
+                                conn.commit()
+                            finally:
+                                cursor.close()
+                                conn.close()
                     
                     return True, f"推送成功!外部项目ID: {remote_project_id or '未知'}"
                 else:
@@ -927,7 +960,5 @@ class TaskService:
         except Exception as e:
             logger.exception(f"推送至外部平台异常: {e}")
             return False, f"推送异常: {str(e)}"
-        finally:
-            conn.close()
 
 task_service = TaskService()

+ 60 - 0
src/app/system/schemas/dict_category_schema.py

@@ -0,0 +1,60 @@
+"""
+字典类型Schema定义
+"""
+from pydantic import BaseModel, Field
+from typing import Optional, List
+from datetime import datetime
+
+
+class DictCategoryBase(BaseModel):
+    """字典类型基础模型"""
+    category_no: Optional[str] = Field(None, max_length=512, description="字典类型编号")
+    category_name: str = Field(..., max_length=512, description="字典类型名称")
+    category_desc: Optional[str] = Field(None, max_length=512, description="字典类型备注")
+    parent_field: str = Field(default="0", max_length=36, description="父节点字典类型id")
+    category_level: Optional[str] = Field(None, max_length=1, description="字典层级")
+
+
+class DictCategoryCreate(DictCategoryBase):
+    """创建字典类型请求模型"""
+    pass
+
+
+class DictCategoryUpdate(BaseModel):
+    """更新字典类型请求模型"""
+    category_no: Optional[str] = Field(None, max_length=512, description="字典类型编号")
+    category_name: Optional[str] = Field(None, max_length=512, description="字典类型名称")
+    category_desc: Optional[str] = Field(None, max_length=512, description="字典类型备注")
+    parent_field: Optional[str] = Field(None, max_length=36, description="父节点字典类型id")
+    category_level: Optional[str] = Field(None, max_length=1, description="字典层级")
+
+
+class DictCategoryResponse(DictCategoryBase):
+    """字典类型响应模型"""
+    category_id: str = Field(..., description="字典类型id")
+    del_flag: str = Field(default="0", description="删除标志位:1是0否")
+    created_by: Optional[str] = Field(None, description="创建人")
+    created_time: Optional[datetime] = Field(None, description="创建时间")
+    updated_by: Optional[str] = Field(None, description="修改人")
+    updated_time: Optional[datetime] = Field(None, description="修改时间")
+    created_by_name: Optional[str] = Field(None, description="创建人姓名")
+    updated_by_name: Optional[str] = Field(None, description="修改人姓名")
+    children: Optional[List['DictCategoryResponse']] = Field(default=[], description="子节点")
+
+    class Config:
+        from_attributes = True
+
+
+class DictCategoryTreeResponse(BaseModel):
+    """字典类型树形结构响应模型"""
+    category_id: str = Field(..., description="字典类型id")
+    category_name: str = Field(..., description="字典类型名称")
+    parent_field: str = Field(..., description="父节点字典类型id")
+    children: Optional[List['DictCategoryTreeResponse']] = Field(default=[], description="子节点")
+
+
+class DictCategoryListRequest(BaseModel):
+    """字典类型列表查询请求"""
+    keyword: Optional[str] = Field(None, description="关键字搜索")
+    page: int = Field(default=1, ge=1, description="页码")
+    page_size: int = Field(default=10, ge=1, le=100, description="每页数量")

+ 61 - 0
src/app/system/schemas/dict_item_schema.py

@@ -0,0 +1,61 @@
+"""
+字典项Schema定义
+"""
+from pydantic import BaseModel, Field
+from typing import Optional
+from datetime import datetime
+
+
+class DictItemBase(BaseModel):
+    """字典项基础模型"""
+    dict_name: str = Field(..., max_length=512, description="字典名称")
+    dict_value: str = Field(..., max_length=512, description="字典值")
+    dict_desc: Optional[str] = Field(None, max_length=512, description="字典备注")
+    category_id: str = Field(..., max_length=255, description="字典类型id")
+    enable_flag: str = Field(default="1", max_length=1, description="启用标志位:1启用0禁用")
+    sort: Optional[int] = Field(None, description="排序")
+
+
+class DictItemCreate(DictItemBase):
+    """创建字典项请求模型"""
+    pass
+
+
+class DictItemUpdate(BaseModel):
+    """更新字典项请求模型"""
+    dict_name: Optional[str] = Field(None, max_length=512, description="字典名称")
+    dict_value: Optional[str] = Field(None, max_length=512, description="字典值")
+    dict_desc: Optional[str] = Field(None, max_length=512, description="字典备注")
+    category_id: Optional[str] = Field(None, max_length=255, description="字典类型id")
+    enable_flag: Optional[str] = Field(None, max_length=1, description="启用标志位:1启用0禁用")
+    sort: Optional[int] = Field(None, description="排序")
+
+
+class DictItemResponse(DictItemBase):
+    """字典项响应模型"""
+    dict_id: int = Field(..., description="字典id")
+    del_flag: str = Field(default="0", description="删除标志位:1是0否")
+    created_by: Optional[str] = Field(None, description="创建人")
+    created_time: Optional[datetime] = Field(None, description="创建时间")
+    updated_by: Optional[str] = Field(None, description="修改人")
+    updated_time: Optional[datetime] = Field(None, description="修改时间")
+    created_by_name: Optional[str] = Field(None, description="创建人姓名")
+    updated_by_name: Optional[str] = Field(None, description="修改人姓名")
+    category_name: Optional[str] = Field(None, description="字典类型名称")
+
+    class Config:
+        from_attributes = True
+
+
+class DictItemListRequest(BaseModel):
+    """字典项列表查询请求"""
+    category_id: Optional[str] = Field(None, description="字典类型id")
+    keyword: Optional[str] = Field(None, description="关键字搜索")
+    enable_flag: Optional[str] = Field(None, description="启用标志位")
+    page: int = Field(default=1, ge=1, description="页码")
+    page_size: int = Field(default=10, ge=1, le=100, description="每页数量")
+
+
+class DictItemBatchDeleteRequest(BaseModel):
+    """批量删除字典项请求"""
+    dict_ids: list[int] = Field(..., description="字典项id列表")

+ 10 - 10
src/views/auth_view.py

@@ -73,7 +73,7 @@ async def login(
         
         logger.info(f"登录成功: username={login_data.username}, user_id={user.id}")
         return ResponseSchema(
-            code=0,
+        code="000000",
             message="登录成功",
             data=token_response.dict()
         )
@@ -88,7 +88,7 @@ async def login(
     except Exception as e:
         logger.error(f"登录错误: {type(e).__name__}: {str(e)}", exc_info=True)
         return ResponseSchema(
-            code=500001,
+        code="500001",
             message=f"服务器内部错误: {str(e)}",
             data=None
         )
@@ -108,7 +108,7 @@ async def refresh_token(
         )
         
         return ResponseSchema(
-            code=0,
+        code="000000",
             message="令牌刷新成功",
             data=token_response.dict()
         )
@@ -121,7 +121,7 @@ async def refresh_token(
         )
     except Exception as e:
         return ResponseSchema(
-            code=500001,
+        code="500001",
             message="服务器内部错误",
             data=None
         )
@@ -154,7 +154,7 @@ async def logout(
         )
         
         return ResponseSchema(
-            code=0,
+        code="000000",
             message="登出成功",
             data=None
         )
@@ -162,7 +162,7 @@ async def logout(
     except Exception as e:
         logger.exception("登出内部错误")
         return ResponseSchema(
-            code=500001,
+        code="500001",
             message="服务器内部错误",
             data=None
         )
@@ -185,7 +185,7 @@ async def get_user_info(
         user_info = await auth_service.get_user_info(user)
         
         response_data = ResponseSchema(
-            code=0,
+        code="000000",
             message="获取用户信息成功",
             data=user_info.dict()
         )
@@ -209,7 +209,7 @@ async def get_user_info(
     except Exception as e:
         logger.exception("获取用户信息内部错误")
         return ResponseSchema(
-            code=500001,
+        code="500001",
             message="服务器内部错误",
             data=None
         )
@@ -265,14 +265,14 @@ async def get_captcha():
         )
         
         return ResponseSchema(
-            code=0,
+        code="000000",
             message="获取验证码成功",
             data=captcha_response.dict()
         )
         
     except Exception as e:
         return ResponseSchema(
-            code=500001,
+        code="500001",
             message="生成验证码失败",
             data=None
         )

+ 229 - 0
src/views/dict_category_view.py

@@ -0,0 +1,229 @@
+"""
+字典类型管理API路由
+"""
+import sys
+import os
+import logging
+from datetime import datetime, timezone
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
+
+from fastapi import APIRouter, Depends, HTTPException
+from app.schemas.base import ApiResponse
+from app.system.schemas.dict_category_schema import (
+    DictCategoryCreate, DictCategoryUpdate, DictCategoryListRequest
+)
+from app.services.dict_category_service import DictCategoryService
+from app.utils.auth_dependency import get_current_user_with_refresh
+
+logger = logging.getLogger(__name__)
+
+router = APIRouter(prefix="/dict/category", tags=["字典类型管理"])
+
+
+@router.get("/tree")
+async def get_category_tree(
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    获取字典类型树形结构
+    """
+    try:
+        service = DictCategoryService()
+        tree = await service.get_category_tree()
+        
+        return ApiResponse(
+            code="000000",
+            message="成功",
+            data=tree,
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+    except Exception as e:
+        logger.exception("获取字典类型树形结构错误")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.post("/list")
+async def get_category_list(
+    request: DictCategoryListRequest,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    获取字典类型列表(分页)
+    """
+    try:
+        service = DictCategoryService()
+        categories, total = await service.get_category_list(
+            page=request.page,
+            page_size=request.page_size,
+            keyword=request.keyword
+        )
+        
+        return ApiResponse(
+            code="000000",
+            message="成功",
+            data={
+                "list": categories,
+                "total": total,
+                "page": request.page,
+                "page_size": request.page_size
+            },
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+    except Exception as e:
+        logger.exception("获取字典类型列表错误")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.get("/{category_id}")
+async def get_category_detail(
+    category_id: str,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    获取字典类型详情
+    """
+    try:
+        service = DictCategoryService()
+        category = await service.get_category_by_id(category_id)
+        
+        if not category:
+            return ApiResponse(
+                code="300001",
+                message="字典类型不存在",
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        
+        return ApiResponse(
+            code="000000",
+            message="成功",
+            data=category,
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+    except Exception as e:
+        logger.exception(f"获取字典类型详情错误: category_id={category_id}")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.post("")
+async def create_category(
+    category: DictCategoryCreate,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    创建字典类型
+    """
+    try:
+        user_id = current_user.get("sub")
+        service = DictCategoryService()
+        
+        success, message, category_id = await service.create_category(
+            category.model_dump(),
+            user_id
+        )
+        
+        if success:
+            return ApiResponse(
+                code="000000",
+                message=message,
+                data={"category_id": category_id},
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        else:
+            return ApiResponse(
+                code="400001",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+    except Exception as e:
+        logger.exception("创建字典类型错误")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.put("/{category_id}")
+async def update_category(
+    category_id: str,
+    category: DictCategoryUpdate,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    更新字典类型
+    """
+    try:
+        user_id = current_user.get("sub")
+        service = DictCategoryService()
+        
+        success, message = await service.update_category(
+            category_id,
+            category.model_dump(exclude_none=True),
+            user_id
+        )
+        
+        if success:
+            return ApiResponse(
+                code="000000",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        else:
+            return ApiResponse(
+                code="400001",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+    except Exception as e:
+        logger.exception(f"更新字典类型错误: category_id={category_id}")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.delete("/{category_id}")
+async def delete_category(
+    category_id: str,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    删除字典类型
+    """
+    try:
+        service = DictCategoryService()
+        success, message = await service.delete_category(category_id)
+        
+        if success:
+            return ApiResponse(
+                code="000000",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        else:
+            return ApiResponse(
+                code="400001",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+    except Exception as e:
+        logger.exception(f"删除字典类型错误: category_id={category_id}")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()

+ 274 - 0
src/views/dict_item_view.py

@@ -0,0 +1,274 @@
+"""
+字典项管理API路由
+"""
+import sys
+import os
+import logging
+from datetime import datetime, timezone
+
+sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../..'))
+
+from fastapi import APIRouter, Depends, HTTPException
+from app.schemas.base import ApiResponse
+from app.system.schemas.dict_item_schema import (
+    DictItemCreate, DictItemUpdate, DictItemListRequest, DictItemBatchDeleteRequest
+)
+from app.services.dict_item_service import DictItemService
+from app.utils.auth_dependency import get_current_user_with_refresh
+
+logger = logging.getLogger(__name__)
+
+router = APIRouter(prefix="/dict/item", tags=["字典项管理"])
+
+
+@router.post("/list")
+async def get_item_list(
+    request: DictItemListRequest,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    获取字典项列表(分页)
+    """
+    try:
+        service = DictItemService()
+        items, total = await service.get_item_list(
+            page=request.page,
+            page_size=request.page_size,
+            category_id=request.category_id,
+            keyword=request.keyword,
+            enable_flag=request.enable_flag
+        )
+        
+        return ApiResponse(
+            code="000000",
+            message="成功",
+            data={
+                "list": items,
+                "total": total,
+                "page": request.page,
+                "page_size": request.page_size
+            },
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+    except Exception as e:
+        logger.exception("获取字典项列表错误")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.get("/{dict_id}")
+async def get_item_detail(
+    dict_id: int,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    获取字典项详情
+    """
+    try:
+        service = DictItemService()
+        item = await service.get_item_by_id(dict_id)
+        
+        if not item:
+            return ApiResponse(
+                code="300001",
+                message="字典项不存在",
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        
+        return ApiResponse(
+            code="000000",
+            message="成功",
+            data=item,
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+    except Exception as e:
+        logger.exception(f"获取字典项详情错误: dict_id={dict_id}")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.post("")
+async def create_item(
+    item: DictItemCreate,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    创建字典项
+    """
+    try:
+        user_id = current_user.get("sub")
+        service = DictItemService()
+        
+        success, message, dict_id = await service.create_item(
+            item.model_dump(),
+            user_id
+        )
+        
+        if success:
+            return ApiResponse(
+                code="000000",
+                message=message,
+                data={"dict_id": dict_id},
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        else:
+            return ApiResponse(
+                code="400001",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+    except Exception as e:
+        logger.exception("创建字典项错误")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.put("/{dict_id}")
+async def update_item(
+    dict_id: int,
+    item: DictItemUpdate,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    更新字典项
+    """
+    try:
+        user_id = current_user.get("sub")
+        service = DictItemService()
+        
+        success, message = await service.update_item(
+            dict_id,
+            item.model_dump(exclude_none=True),
+            user_id
+        )
+        
+        if success:
+            return ApiResponse(
+                code="000000",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        else:
+            return ApiResponse(
+                code="400001",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+    except Exception as e:
+        logger.exception(f"更新字典项错误: dict_id={dict_id}")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.delete("/{dict_id}")
+async def delete_item(
+    dict_id: int,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    删除字典项
+    """
+    try:
+        service = DictItemService()
+        success, message = await service.delete_item(dict_id)
+        
+        if success:
+            return ApiResponse(
+                code="000000",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        else:
+            return ApiResponse(
+                code="400001",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+    except Exception as e:
+        logger.exception(f"删除字典项错误: dict_id={dict_id}")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.post("/batch-delete")
+async def batch_delete_items(
+    request: DictItemBatchDeleteRequest,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    批量删除字典项
+    """
+    try:
+        service = DictItemService()
+        success, message = await service.batch_delete_items(request.dict_ids)
+        
+        if success:
+            return ApiResponse(
+                code="000000",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        else:
+            return ApiResponse(
+                code="400001",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+    except Exception as e:
+        logger.exception("批量删除字典项错误")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+
+
+@router.put("/{dict_id}/toggle-status")
+async def toggle_item_status(
+    dict_id: int,
+    enable_flag: str,
+    current_user: dict = Depends(get_current_user_with_refresh)
+):
+    """
+    切换字典项启用状态
+    """
+    try:
+        user_id = current_user.get("sub")
+        service = DictItemService()
+        
+        success, message = await service.toggle_item_status(dict_id, enable_flag, user_id)
+        
+        if success:
+            return ApiResponse(
+                code="000000",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+        else:
+            return ApiResponse(
+                code="400001",
+                message=message,
+                timestamp=datetime.now(timezone.utc).isoformat()
+            ).model_dump()
+    except Exception as e:
+        logger.exception(f"切换字典项状态错误: dict_id={dict_id}")
+        return ApiResponse(
+            code="500001",
+            message="服务器内部错误",
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()

+ 68 - 24
src/views/image_view.py

@@ -2,7 +2,7 @@ import logging
 from datetime import datetime, timezone
 from typing import Optional, List, Dict, Any
 
-from fastapi import APIRouter, Depends, HTTPException, Request, Response
+from fastapi import APIRouter, Depends, HTTPException, Request, Response, Query
 from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
 
 from app.services.image_service import ImageService
@@ -44,6 +44,11 @@ class BatchAddRequest(BaseModel):
     project_name: str
     tags: Optional[List[str]] = None
 
+class CategoryBatchAddRequest(BaseModel):
+    category_id: str
+    project_name: str
+    tags: Optional[List[str]] = None
+
 # --- 分类管理 API ---
 
 @router.get("/categories")
@@ -52,10 +57,10 @@ async def get_categories(current_user: dict = Depends(get_current_user_with_refr
     try:
         service = ImageService()
         data = await service.get_categories()
-        return ApiResponse(code=0, message="成功", data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="000000", message="成功", data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("获取分类失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/categories")
 async def add_category(data: CategoryAdd, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -66,12 +71,12 @@ async def add_category(data: CategoryAdd, current_user: dict = Depends(get_curre
         success, message, category_id = await service.add_category(data.model_dump(), user_id)
         
         if success:
-            return ApiResponse(code=0, message=message, data={"id": category_id}, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, data={"id": category_id}, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("新增分类失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.put("/categories/{category_id}")
 async def edit_category(category_id: str, data: CategoryUpdate, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -82,12 +87,12 @@ async def edit_category(category_id: str, data: CategoryUpdate, current_user: di
         success, message = await service.edit_category(category_id, data.model_dump(), user_id)
         
         if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("编辑分类失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.delete("/categories/{category_id}")
 async def delete_category(category_id: str, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -97,12 +102,12 @@ async def delete_category(category_id: str, current_user: dict = Depends(get_cur
         success, message = await service.delete_category(category_id)
         
         if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=400, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="400001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("删除分类失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 # --- 图片管理 API ---
 
@@ -117,10 +122,10 @@ async def get_image_list(
     try:
         service = ImageService()
         data = await service.get_image_list(category_id, page, page_size)
-        return ApiResponse(code=0, message="成功", data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="000000", message="成功", data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("获取图片列表失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("")
 async def add_image(data: ImageAdd, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -131,12 +136,12 @@ async def add_image(data: ImageAdd, current_user: dict = Depends(get_current_use
         success, message = await service.add_image(data.model_dump(), user_id)
         
         if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("保存图片失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.delete("/{image_id}")
 async def delete_image(image_id: str, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -146,12 +151,12 @@ async def delete_image(image_id: str, current_user: dict = Depends(get_current_u
         success, message = await service.delete_image(image_id)
         
         if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("删除图片失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/upload-url")
 async def get_upload_url(req: UploadUrlRequest, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -161,12 +166,12 @@ async def get_upload_url(req: UploadUrlRequest, current_user: dict = Depends(get
         success, message, data = await service.get_upload_url(req.filename, req.content_type, prefix=req.prefix)
         
         if success:
-            return ApiResponse(code=0, message=message, data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("获取上传链接失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/batch-add-to-task")
 async def batch_add_to_task(req: BatchAddRequest, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -185,4 +190,43 @@ async def batch_add_to_task(req: BatchAddRequest, current_user: dict = Depends(g
         ).model_dump()
     except Exception as e:
         logger.exception("批量加入任务失败")
-        return ApiResponse(code=500, message=f"批量加入任务失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=f"批量加入任务失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+
+@router.post("/category-batch-add-to-task")
+async def category_batch_add_to_task(req: CategoryBatchAddRequest, current_user: dict = Depends(get_current_user_with_refresh)):
+    """按分类批量加入任务中心"""
+    try:
+        user_id = current_user.get("sub", "admin")
+        username = current_user.get("username", user_id)
+        
+        service = ImageService()
+        success, message, data = await service.category_batch_add_to_task(
+            req.category_id, username, req.project_name, tags=req.tags
+        )
+        
+        return ApiResponse(
+            code=0 if success else 500, 
+            message=message, 
+            data=data,
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+    except Exception as e:
+        logger.exception("按分类批量加入任务失败")
+        return ApiResponse(code="500001", message=f"操作失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+
+@router.get("/category-batch-check")
+async def category_batch_check(category_id: str = Query("", description="分类ID"), current_user: dict = Depends(get_current_user_with_refresh)):
+    """按分类批量推送前的预检查"""
+    try:
+        service = ImageService()
+        success, message, data = await service.category_batch_check(category_id)
+        
+        return ApiResponse(
+            code=0 if success else 500, 
+            message=message, 
+            data=data,
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+    except Exception as e:
+        logger.exception("按分类批量检查失败")
+        return ApiResponse(code="500001", message=f"操作失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()

+ 16 - 16
src/views/knowledge_base_view.py

@@ -35,7 +35,7 @@ async def get_knowledge_bases(
     )
 
     return PaginatedResponseSchema(
-        code=0,
+        code="000000",
         message="获取知识库列表成功",
         data=[KnowledgeBaseResponse.model_validate(item) for item in items],
         meta=meta,
@@ -50,7 +50,7 @@ async def get_knowledge_base_simple_list(
     # 只获取状态正常的知识库
     items, _ = await knowledge_base_service.get_list(db, page=1, page_size=1000, status="normal")
     return ResponseSchema(
-        code=0,
+        code="000000",
         message="获取成功",
         data=[{"id": item.id, "name": item.name, "collection_name": item.collection_name_children or item.collection_name_parent} for item in items]
     )
@@ -64,11 +64,11 @@ async def create_knowledge_base(
     """创建新知识库"""
     try:
         new_kb = await knowledge_base_service.create(db, payload)
-        return ResponseSchema(code=0, message="创建成功", data=KnowledgeBaseResponse.model_validate(new_kb))
+        return ResponseSchema(code="000000", message="创建成功", data=KnowledgeBaseResponse.model_validate(new_kb))
     except ValueError as e:
-        return ResponseSchema(code=400, message=str(e))
+        return ResponseSchema(code="400001", message=str(e))
     except Exception:
-        return ResponseSchema(code=500, message="创建失败")
+        return ResponseSchema(code="500001", message="创建失败")
 
 @router.post("/{id}", response_model=ResponseSchema)
 async def update_knowledge_base(
@@ -80,11 +80,11 @@ async def update_knowledge_base(
     """更新知识库信息"""
     try:
         kb = await knowledge_base_service.update(db, id, payload)
-        return ResponseSchema(code=0, message="更新成功", data=KnowledgeBaseResponse.model_validate(kb))
+        return ResponseSchema(code="000000", message="更新成功", data=KnowledgeBaseResponse.model_validate(kb))
     except ValueError as e:
-        return ResponseSchema(code=400, message=str(e))
+        return ResponseSchema(code="400001", message=str(e))
     except Exception:
-        return ResponseSchema(code=500, message="更新失败")
+        return ResponseSchema(code="500001", message="更新失败")
 
 @router.post("/{id}/status", response_model=ResponseSchema)
 async def update_knowledge_base_status(
@@ -95,7 +95,7 @@ async def update_knowledge_base_status(
 ):
     """更新知识库状态"""
     await knowledge_base_service.update_status(db, id, status)
-    return ResponseSchema(code=0, message=f"状态已更新为 {status}")
+    return ResponseSchema(code="000000", message=f"状态已更新为 {status}")
 
 @router.post("/{id}/delete", response_model=ResponseSchema)
 async def delete_knowledge_base(
@@ -105,7 +105,7 @@ async def delete_knowledge_base(
 ):
     """删除知识库"""
     await knowledge_base_service.delete(db, id)
-    return ResponseSchema(code=0, message="删除成功")
+    return ResponseSchema(code="000000", message="删除成功")
 
 @router.get("/{id}/metadata", response_model=ResponseSchema)
 async def get_knowledge_base_metadata(
@@ -116,11 +116,11 @@ async def get_knowledge_base_metadata(
     """获取知识库的元数据字段定义和自定义Schema"""
     try:
         data = await knowledge_base_service.get_metadata_and_schema(db, id)
-        return ResponseSchema(code=0, message="获取成功", data=data)
+        return ResponseSchema(code="000000", message="获取成功", data=data)
     except ValueError as e:
-        return ResponseSchema(code=400, message=str(e))
+        return ResponseSchema(code="400001", message=str(e))
     except Exception as e:
-        return ResponseSchema(code=500, message=f"获取失败: {str(e)}")
+        return ResponseSchema(code="500001", message=f"获取失败: {str(e)}")
 
 @router.post("/{id}/sync", response_model=ResponseSchema)
 async def sync_knowledge_base(
@@ -131,8 +131,8 @@ async def sync_knowledge_base(
     """同步创建Milvus集合"""
     try:
         await knowledge_base_service.sync_to_milvus(db, id)
-        return ResponseSchema(code=0, message="同步成功")
+        return ResponseSchema(code="000000", message="同步成功")
     except ValueError as e:
-        return ResponseSchema(code=400, message=str(e))
+        return ResponseSchema(code="400001", message=str(e))
     except Exception as e:
-        return ResponseSchema(code=500, message=f"同步失败: {str(e)}")
+        return ResponseSchema(code="500001", message=f"同步失败: {str(e)}")

+ 93 - 73
src/views/sample_view.py

@@ -11,7 +11,7 @@ from fastapi import APIRouter, Depends, HTTPException, Request, Response, Backgr
 from fastapi.responses import HTMLResponse, StreamingResponse
 from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
 
-from app.sample.schemas.sample_schemas import BatchEnterRequest, BatchDeleteRequest, ConvertRequest, DocumentAdd, UploadUrlRequest, ExportRequest
+from app.sample.schemas.sample_schemas import BatchEnterRequest, BatchDeleteRequest, ConvertRequest, DocumentAdd, UploadUrlRequest, ExportRequest, BatchClearRequest
 from app.services.sample_service import SampleService
 from app.services.jwt_token import verify_token
 from app.schemas.base import ApiResponse
@@ -34,10 +34,10 @@ async def get_tasks(type: str, current_user: dict = Depends(get_current_user_wit
     """获取任务项目列表 (聚合显示)"""
     try:
         projects = await task_service.get_task_list(type)
-        return ApiResponse(code=0, message="成功", data=projects).model_dump()
+        return ApiResponse(code="000000", message="成功", data=projects).model_dump()
     except Exception as e:
         logger.exception("获取项目列表失败")
-        return ApiResponse(code=500, message=str(e)).model_dump()
+        return ApiResponse(code="500001", message=str(e)).model_dump()
 
 
 @router.get("/tasks/details")
@@ -45,10 +45,10 @@ async def get_task_details(project_id: str, type: str, current_user: dict = Depe
     """获取项目下的文件详情"""
     try:
         files = await task_service.get_project_details(project_id, type)
-        return ApiResponse(code=0, message="成功", data=files).model_dump()
+        return ApiResponse(code="000000", message="成功", data=files).model_dump()
     except Exception as e:
         logger.exception("获取项目详情失败")
-        return ApiResponse(code=500, message=str(e)).model_dump()
+        return ApiResponse(code="500001", message=str(e)).model_dump()
 
 
 # --- 外部联动接口 API ---
@@ -59,18 +59,18 @@ async def init_external_project(request: Request, current_user: dict = Depends(g
     try:
         # 简单验证是否为管理员(根据业务需求调整)
         if not current_user.get("is_superuser") and current_user.get("role") != "admin":
-            return ApiResponse(code=403, message="权限不足").model_dump()
+            return ApiResponse(code="200003", message="权限不足").model_dump()
 
         data = await request.json()
         success, result = await task_service.create_anno_project(data)
         
         if success:
-            return ApiResponse(code=0, message="项目初始化成功", data={"project_id": result}).model_dump()
+            return ApiResponse(code="000000", message="项目初始化成功", data={"project_id": result}).model_dump()
         else:
-            return ApiResponse(code=500, message=f"项目初始化失败: {result}").model_dump()
+            return ApiResponse(code="500001", message=f"项目初始化失败: {result}").model_dump()
     except Exception as e:
         logger.exception("项目初始化接口异常")
-        return ApiResponse(code=500, message=str(e)).model_dump()
+        return ApiResponse(code="500001", message=str(e)).model_dump()
 
 @router.get("/external/projects/{project_id}/progress")
 @router.get("/external/projects/progress")
@@ -81,16 +81,16 @@ async def get_external_project_progress(
     """查询项目进度"""
     try:
         if not project_id:
-            return ApiResponse(code=400, message="缺少项目ID").model_dump()
+            return ApiResponse(code="400001", message="缺少项目ID").model_dump()
 
         progress = await task_service.get_project_progress(project_id=project_id)
         if "error" in progress:
-            return ApiResponse(code=500, message=progress["error"]).model_dump()
+            return ApiResponse(code="500001", message=progress["error"]).model_dump()
             
-        return ApiResponse(code=0, message="成功", data=progress).model_dump()
+        return ApiResponse(code="000000", message="成功", data=progress).model_dump()
     except Exception as e:
         logger.exception("查询进度接口异常")
-        return ApiResponse(code=500, message=str(e)).model_dump()
+        return ApiResponse(code="500001", message=str(e)).model_dump()
 
 @router.post("/external/projects/{project_id}/export")
 @router.post("/external/projects/export")
@@ -106,7 +106,7 @@ async def export_external_project(
         actual_project_id = project_id or req.project_id
         if not actual_project_id:
             logger.warning("导出请求缺少项目ID")
-            return ApiResponse(code=400, message="缺少项目ID").model_dump()
+            return ApiResponse(code="400001", message="缺少项目ID").model_dump()
             
         logger.info(f"正在为项目 {actual_project_id} 执行导出,格式: {req.format}")
         result = await task_service.export_labeled_data(
@@ -117,13 +117,13 @@ async def export_external_project(
         
         if "error" in result:
             logger.error(f"导出执行失败: {result['error']}")
-            return ApiResponse(code=500, message=result["error"]).model_dump()
+            return ApiResponse(code="500001", message=result["error"]).model_dump()
             
         logger.info(f"项目 {actual_project_id} 导出成功")
-        return ApiResponse(code=0, message="成功", data=result).model_dump()
+        return ApiResponse(code="000000", message="成功", data=result).model_dump()
     except Exception as e:
         logger.exception("导出数据接口异常")
-        return ApiResponse(code=500, message=str(e)).model_dump()
+        return ApiResponse(code="500001", message=str(e)).model_dump()
 
 @router.get("/external/download-proxy")
 async def download_proxy(url: str, filename: str, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -178,7 +178,7 @@ async def download_proxy(url: str, filename: str, current_user: dict = Depends(g
         )
     except Exception as e:
         logger.exception("下载代理异常")
-        return ApiResponse(code=500, message=str(e)).model_dump()
+        return ApiResponse(code="500001", message=str(e)).model_dump()
 
 @router.post("/documents/upload-url")
 async def get_upload_url(req: UploadUrlRequest, credentials: dict = Depends(get_current_user_with_refresh)):
@@ -188,12 +188,12 @@ async def get_upload_url(req: UploadUrlRequest, credentials: dict = Depends(get_
         success, message, data = await sample_service.get_upload_url(req.filename, req.content_type, prefix=req.prefix)
         
         if success:
-            return ApiResponse(code=0, message=message, data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, data=data, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("获取上传链接失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.get("/documents/proxy-view")
 async def proxy_view(url: str, token: Optional[str] = None, credentials: Optional[HTTPAuthorizationCredentials] = Depends(security_optional)):
@@ -210,7 +210,7 @@ async def proxy_view(url: str, token: Optional[str] = None, credentials: Optiona
             actual_token = token
 
         if not actual_token:
-            return ApiResponse(code=401, message="未提供认证令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="200002", message="未提供认证令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
             
         # 增加超时时间,支持大文件下载
@@ -303,7 +303,7 @@ async def download_document(url: str, filename: Optional[str] = None, token: Opt
     """代理下载云端文件,支持从 MinIO 等外部地址下载"""
     try:
         if not url:
-             return ApiResponse(code=400, message="缺少URL参数", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+             return ApiResponse(code="400001", message="缺少URL参数", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         # 确保 URL 已解码
         url = urllib.parse.unquote(url)
@@ -316,7 +316,7 @@ async def download_document(url: str, filename: Optional[str] = None, token: Opt
             actual_token = token
             
         if not actual_token:
-            return ApiResponse(code=401, message="未提供认证令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="200002", message="未提供认证令牌", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
             
         # 增加超时时间,支持大文件下载
@@ -343,7 +343,7 @@ async def download_document(url: str, filename: Optional[str] = None, token: Opt
                 
     except Exception as e:
         logger.exception(f"文件下载失败, url={url}")
-        return ApiResponse(code=500, message=f"下载失败: {str(e)} (URL: {url})", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=f"下载失败: {str(e)} (URL: {url})", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/documents/batch-enter")
 async def batch_enter_knowledge_base(req: BatchEnterRequest, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -352,7 +352,7 @@ async def batch_enter_knowledge_base(req: BatchEnterRequest, current_user: dict
         
         username = current_user.get("sub")
         if not username:
-            return ApiResponse(code=401, message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="200002", message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         sample_service = SampleService()
         
@@ -365,11 +365,31 @@ async def batch_enter_knowledge_base(req: BatchEnterRequest, current_user: dict
         )
         
         # 如果全部失败,返回非零状态码,触发前端错误提示
-        code = 0 if success_count > 0 else 1
+        code = "000000" if success_count > 0 else "000001"
         return ApiResponse(code=code, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("批量操作失败")
-        return ApiResponse(code=500, message=f"批量操作失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=f"批量操作失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+
+@router.post("/documents/batch-clear")
+async def batch_clear_knowledge_base(req: BatchClearRequest, current_user: dict = Depends(get_current_user_with_refresh)):
+    """批量清空文档在知识库中的数据片段"""
+    try:
+        username = current_user.get("sub")
+        if not username:
+            return ApiResponse(code="200002", message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        
+        sample_service = SampleService()
+        success_count, message = await sample_service.clear_knowledge_base_data(req.ids, username)
+        
+        return ApiResponse(
+            code = "000000" if success_count > 0 else "000001", 
+            message=message, 
+            timestamp=datetime.now(timezone.utc).isoformat()
+        ).model_dump()
+    except Exception as e:
+        logger.exception("批量清空失败")
+        return ApiResponse(code="500001", message=f"批量清空失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/documents/batch-delete")
 async def batch_delete_documents(req: BatchDeleteRequest, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -380,13 +400,13 @@ async def batch_delete_documents(req: BatchDeleteRequest, current_user: dict = D
         affected_rows, message = await sample_service.batch_delete_documents(req.ids)
         
         return ApiResponse(
-            code=0, 
+        code="000000", 
             message=message, 
             timestamp=datetime.now(timezone.utc).isoformat()
         ).model_dump()
     except Exception as e:
         logger.exception("批量删除失败")
-        return ApiResponse(code=500, message=f"批量删除失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=f"批量删除失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 from pydantic import BaseModel
 
@@ -402,7 +422,7 @@ async def batch_add_to_task(req: BatchAddTaskRequest, current_user: dict = Depen
         
         user_id = current_user.get("sub")
         if not user_id:
-            return ApiResponse(code=401, message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="200002", message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         username = current_user.get("username", user_id)
         
@@ -410,13 +430,13 @@ async def batch_add_to_task(req: BatchAddTaskRequest, current_user: dict = Depen
         success, message = await sample_service.batch_add_to_task(req.doc_ids, username, req.project_name, task_tags=req.tags)
         
         return ApiResponse(
-            code=0 if success else 500, 
+            code = "000000" if success > 0 else "000500", 
             message=message, 
             timestamp=datetime.now(timezone.utc).isoformat()
         ).model_dump()
     except Exception as e:
         logger.exception("批量加入任务失败")
-        return ApiResponse(code=500, message=f"批量加入任务失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=f"批量加入任务失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/documents/convert")
 async def convert_document(req: ConvertRequest, background_tasks: BackgroundTasks, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -429,7 +449,7 @@ async def convert_document(req: ConvertRequest, background_tasks: BackgroundTask
         # 1. 获取文档详情以取得 title 和 file_url
         doc = await sample_service.get_document_detail(doc_id)
         if not doc:
-            return ApiResponse(code=404, message="文档不存在", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="300001", message="文档不存在", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         title = doc.get("title")
         file_url = doc.get("file_url")
@@ -438,13 +458,13 @@ async def convert_document(req: ConvertRequest, background_tasks: BackgroundTask
         
         # 2. 检查当前状态,避免重复请求
         if status == 1:
-            return ApiResponse(code=0, message="文档正在转换中,请勿重复操作", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message="文档正在转换中,请勿重复操作", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         if status == 2:
-            return ApiResponse(code=0, message="文档已转换完成", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message="文档已转换完成", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         if not file_url:
-            return ApiResponse(code=400, message="文档缺少文件链接,无法转换", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="400001", message="文档缺少文件链接,无法转换", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
         # 3. 立即将状态更新为“转换中”,避免前端轮询延迟
         await sample_service.update_conversion_status(doc_id, status=1)
@@ -454,13 +474,13 @@ async def convert_document(req: ConvertRequest, background_tasks: BackgroundTask
         background_tasks.add_task(manager.process_document, doc_id, title, file_url, table_type)
         
         return ApiResponse(
-            code=0, 
+        code="000000", 
             message="转换任务已在后台启动", 
             timestamp=datetime.now(timezone.utc).isoformat()
         ).model_dump()
     except Exception as e:
         logger.exception("启动转换失败")
-        return ApiResponse(code=500, message=f"启动转换失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=f"启动转换失败: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/documents/add")
 async def add_document(doc: DocumentAdd, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -469,7 +489,7 @@ async def add_document(doc: DocumentAdd, current_user: dict = Depends(get_curren
         
         user_id = current_user.get("sub")
         if not user_id:
-            return ApiResponse(code=401, message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="200002", message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         sample_service = SampleService()
         
@@ -479,12 +499,12 @@ async def add_document(doc: DocumentAdd, current_user: dict = Depends(get_curren
         success, message, doc_id = await sample_service.add_document(doc_data, user_id)
         
         if success:
-            return ApiResponse(code=0, message=message, data={"id": doc_id}, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, data={"id": doc_id}, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("添加文档失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.get("/documents/detail/{doc_id}")
 async def get_document_detail(doc_id: str, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -495,13 +515,13 @@ async def get_document_detail(doc_id: str, current_user: dict = Depends(get_curr
         doc = await sample_service.get_document_detail(doc_id)
         
         if not doc:
-            return ApiResponse(code=404, message="文档不存在", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="300001", message="文档不存在", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
-        return ApiResponse(code=0, message="获取详情成功", data=doc, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="000000", message="获取详情成功", data=doc, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
     except Exception as e:
         logger.exception("获取文档详情失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.get("/documents/list")
 async def get_document_list(
@@ -535,7 +555,7 @@ async def get_document_list(
         )
         
         return ApiResponse(
-            code=0, 
+        code="000000", 
             message="查询成功", 
             data={
                 "items": items, 
@@ -549,7 +569,7 @@ async def get_document_list(
         ).model_dump()
     except Exception as e:
         logger.exception("获取文档列表失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/documents/edit")
 async def edit_document(doc: DocumentAdd, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -557,7 +577,7 @@ async def edit_document(doc: DocumentAdd, current_user: dict = Depends(get_curre
     try:
             
         if not doc.id:
-            return ApiResponse(code=400, message="缺少ID参数", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="400001", message="缺少ID参数", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         # 调用 service 层
         sample_service = SampleService()
@@ -571,12 +591,12 @@ async def edit_document(doc: DocumentAdd, current_user: dict = Depends(get_curre
         success, message = await sample_service.edit_document(doc_data, updater_id)
         
         if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("编辑文档失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/documents/enter")
 async def enter_document(data: dict, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -584,7 +604,7 @@ async def enter_document(data: dict, current_user: dict = Depends(get_current_us
     try:
         doc_id = data.get("id")
         if not doc_id:
-            return ApiResponse(code=400, message="缺少ID", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="400001", message="缺少ID", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
             
         username = current_user.get("sub", "admin") if payload else "admin"
         
@@ -593,12 +613,12 @@ async def enter_document(data: dict, current_user: dict = Depends(get_current_us
         success, message = await sample_service.enter_document(doc_id, username)
         
         if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("入库失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 
 
@@ -666,7 +686,7 @@ async def get_basic_info_list(
         )
         
         return ApiResponse(
-            code=0,
+        code="000000",
             message="查询成功",
             data={"items": items, "total": total, "page": page, "size": size},
             timestamp=datetime.now(timezone.utc).isoformat()
@@ -674,7 +694,7 @@ async def get_basic_info_list(
         
     except Exception as e:
         logger.exception("查询基本信息失败")
-        return ApiResponse(code=500, message=f"服务器内部错误: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=f"服务器内部错误: {str(e)}", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/basic-info/add")
 async def add_basic_info(type: str, data: dict, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -683,18 +703,18 @@ async def add_basic_info(type: str, data: dict, current_user: dict = Depends(get
         
         user_id = current_user.get("sub")
         if not user_id:
-            return ApiResponse(code=401, message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="200002", message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         
         sample_service = SampleService()
         success, message, doc_id = await sample_service.add_basic_info(type, data, user_id)
         
         if success:
-            return ApiResponse(code=0, message=message, data={"id": doc_id}, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, data={"id": doc_id}, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("新增基本信息失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/basic-info/edit")
 async def edit_basic_info(type: str, id: str, data: dict, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -703,18 +723,18 @@ async def edit_basic_info(type: str, id: str, data: dict, current_user: dict = D
         
         user_id = current_user.get("sub")
         if not user_id:
-            return ApiResponse(code=401, message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="200002", message="令牌中缺少用户信息", timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
             
         sample_service = SampleService()
         success, message = await sample_service.edit_basic_info(type, id, data, user_id)
         
         if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("编辑基本信息失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.post("/basic-info/delete")
 async def delete_basic_info(type: str, id: str, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -725,12 +745,12 @@ async def delete_basic_info(type: str, id: str, current_user: dict = Depends(get
         success, message = await sample_service.delete_basic_info(type, id)
         
         if success:
-            return ApiResponse(code=0, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="000000", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
         else:
-            return ApiResponse(code=500, message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+            return ApiResponse(code="500001", message=message, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
         logger.exception("删除基本信息失败")
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.get("/documents/categories/primary")
 async def get_primary_categories(current_user: dict = Depends(get_current_user_with_refresh)):
@@ -740,9 +760,9 @@ async def get_primary_categories(current_user: dict = Depends(get_current_user_w
         # 仅保留用户要求的分类
         default_categories = ["办公制度", "行业标准", "法律法规", "施工方案", "施工图片"]
         categories = [{"id": name, "name": name} for name in default_categories]
-        return ApiResponse(code=0, message="获取成功", data=categories, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="000000", message="获取成功", data=categories, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.get("/documents/categories/secondary")
 async def get_secondary_categories(primaryId: str, current_user: dict = Depends(get_current_user_with_refresh)):
@@ -755,9 +775,9 @@ async def get_secondary_categories(primaryId: str, current_user: dict = Depends(
             secondary_names = ["采购", "报销", "审批"]
             categories = [{"id": name, "name": name} for name in secondary_names]
         
-        return ApiResponse(code=0, message="获取成功", data=categories, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="000000", message="获取成功", data=categories, timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
     except Exception as e:
-        return ApiResponse(code=500, message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
+        return ApiResponse(code="500001", message=str(e), timestamp=datetime.now(timezone.utc).isoformat()).model_dump()
 
 @router.get("/documents/search")
 async def search_documents(

+ 8 - 8
src/views/search_engine_view.py

@@ -31,11 +31,11 @@ async def search_knowledge_base(
     """知识库语义搜索"""
     try:
         result = await search_engine_service.search_kb(db, payload)
-        return ResponseSchema(code=0, message="搜索成功", data=result)
+        return ResponseSchema(code="000000", message="搜索成功", data=result)
     except ValueError as e:
-        return ResponseSchema(code=400, message=str(e), data=KBSearchResponse(results=[], total=0))
+        return ResponseSchema(code="400001", message=str(e), data=KBSearchResponse(results=[], total=0))
     except Exception:
-        return ResponseSchema(code=500, message="搜索失败", data=KBSearchResponse(results=[], total=0))
+        return ResponseSchema(code="500001", message="搜索失败", data=KBSearchResponse(results=[], total=0))
 
 
 # --- 原有接口 ---
@@ -54,7 +54,7 @@ async def get_search_engines(
     )
 
     return PaginatedResponseSchema(
-        code=0,
+        code="000000",
         message="获取检索引擎列表成功",
         data=[SearchEngineResponse.model_validate(item) for item in items],
         meta=meta,
@@ -68,7 +68,7 @@ async def create_search_engine(
 ):
     """创建新检索引擎"""
     new_engine = await search_engine_service.create(db, payload)
-    return ResponseSchema(code=0, message="创建成功", data=SearchEngineResponse.model_validate(new_engine))
+    return ResponseSchema(code="000000", message="创建成功", data=SearchEngineResponse.model_validate(new_engine))
 
 @router.post("/{id}", response_model=ResponseSchema)
 async def update_search_engine(
@@ -79,7 +79,7 @@ async def update_search_engine(
 ):
     """更新检索引擎信息"""
     engine = await search_engine_service.update(db, id, payload)
-    return ResponseSchema(code=0, message="更新成功", data=SearchEngineResponse.model_validate(engine))
+    return ResponseSchema(code="000000", message="更新成功", data=SearchEngineResponse.model_validate(engine))
 
 @router.post("/{id}/status", response_model=ResponseSchema)
 async def update_search_engine_status(
@@ -90,7 +90,7 @@ async def update_search_engine_status(
 ):
     """更新检索引擎状态"""
     await search_engine_service.update_status(db, id, status)
-    return ResponseSchema(code=0, message=f"状态已更新为 {status}")
+    return ResponseSchema(code="000000", message=f"状态已更新为 {status}")
 
 @router.post("/{id}/delete", response_model=ResponseSchema)
 async def delete_search_engine(
@@ -100,4 +100,4 @@ async def delete_search_engine(
 ):
     """删除检索引擎"""
     await search_engine_service.delete(db, id)
-    return ResponseSchema(code=0, message="删除成功")
+    return ResponseSchema(code="000000", message="删除成功")

+ 7 - 7
src/views/snippet_view.py

@@ -47,7 +47,7 @@ async def get_snippets(
     items, meta = await snippet_service.get_list(page, page_size, kb, keyword, status)
     
     return PaginatedResponseSchema(
-        code=0, 
+        code="000000", 
         message="获取成功", 
         data=items, 
         meta=meta
@@ -62,7 +62,7 @@ async def get_document_chunks(
     chunks = await snippet_service.get_chunks_by_document(doc_id)
     
     return ResponseSchema(
-        code=0, 
+        code="000000", 
         message="获取成功", 
         data=chunks
     )
@@ -77,9 +77,9 @@ async def get_snippet_detail(
     """获取知识片段详情"""
     data = await snippet_service.get_by_id(db, kb, id)
     if not data:
-        return ResponseSchema(code=404, message="未找到该片段")
+        return ResponseSchema(code="300001", message="未找到该片段")
         
-    return ResponseSchema(code=0, message="获取成功", data=data)
+    return ResponseSchema(code="000000", message="获取成功", data=data)
 
 @router.get("/export")
 async def export_snippets(
@@ -108,7 +108,7 @@ async def create_snippet(
 ):
     """创建知识片段"""
     data = await snippet_service.create(db, payload)
-    return ResponseSchema(code=0, message="创建成功", data=data)
+    return ResponseSchema(code="000000", message="创建成功", data=data)
 
 @router.post("/{id}", response_model=ResponseSchema)
 async def update_snippet(
@@ -119,7 +119,7 @@ async def update_snippet(
 ):
     """更新知识片段"""
     msg = await snippet_service.update(db, id, payload)
-    return ResponseSchema(code=0, message=msg)
+    return ResponseSchema(code="000000", message=msg)
 
 @router.post("/{id}/delete", response_model=ResponseSchema)
 async def delete_snippet(
@@ -134,4 +134,4 @@ async def delete_snippet(
     # 更新知识库文档数量并返回最新计数,便于前端立即刷新展示
     new_count = await knowledge_base_service.update_doc_count(db, kb)
 
-    return ResponseSchema(code=0, message="删除成功", data={"document_count": new_count})
+    return ResponseSchema(code="000000", message="删除成功", data={"document_count": new_count})

Разница между файлами не показана из-за своего большого размера
+ 128 - 128
src/views/system_view.py


+ 22 - 22
src/views/tag_view.py

@@ -1,6 +1,6 @@
 """
 标签分类视图路由
-处理标签分类API 接口
+处理标签分类�?API 接口
 """
 from fastapi import APIRouter, Query, Path, Depends, Request
 from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
@@ -43,7 +43,7 @@ async def create_tag_category(
     await session.commit()
     
     return ResponseSchema(
-        code=200,
+        code="000000",
         message="创建标签分类成功",
         data=result.model_dump()
     )
@@ -59,7 +59,7 @@ async def get_tag_category(
     result = await service.get_tag_category_by_id(category_id)
     
     return ResponseSchema(
-        code=200,
+        code="000000",
         message="获取成功",
         data=result.model_dump() if result else None
     )
@@ -68,7 +68,7 @@ async def get_tag_category(
 @router.get("/list", response_model=PaginatedResponseSchema)
 async def list_tag_categories(
     parent_id: int = Query(None, description="父级分类ID"),
-    status: int = Query(None, description="状态筛选"),
+    status: int = Query(None, description="状态"),
     page: int = Query(1, ge=1, description="页码"),
     page_size: int = Query(10, ge=1, le=1000, description="每页数量"),
     session: AsyncSession = Depends(get_db)
@@ -85,7 +85,7 @@ async def list_tag_categories(
     )
     
     return PaginatedResponseSchema(
-        code=200,
+        code="000000",
         message="获取标签分类列表成功",
         data=[cat.model_dump() for cat in categories],
         meta={
@@ -108,7 +108,7 @@ async def update_tag_category(
     await session.commit()
     
     return ResponseSchema(
-        code=200,
+        code="000000",
         message="更新标签分类成功",
         data=result.model_dump()
     )
@@ -117,7 +117,7 @@ async def update_tag_category(
 @router.post("/delete/{category_id}", response_model=ResponseSchema)
 async def delete_tag_category(
     category_id: int = Path(..., description="分类ID"),
-    soft_delete: bool = Query(True, description="是否删除"),
+    soft_delete: bool = Query(True, description="是否删除"),
     session: AsyncSession = Depends(get_db)
 ):
     """删除标签分类"""
@@ -126,7 +126,7 @@ async def delete_tag_category(
     await session.commit()
     
     return ResponseSchema(
-        code=200,
+        code="000000",
         message="删除标签分类成功"
     )
 
@@ -136,19 +136,19 @@ async def get_category_tree(
     include_disabled: bool = Query(False, description="是否包含禁用分类"),
     session: AsyncSession = Depends(get_db)
 ):
-    """获取标签分类树(层级结构"""
+    """获取标签分类树(层级结构"""
     service = TagCategoryService(session)
     tree = await service.get_category_tree(root_id=0, include_disabled=include_disabled)
     
     return ResponseSchema(
-        code=200,
-        message="获取分类树成",
+        code="000000",
+        message="获取分类树成",
         data=[_build_tree_response(node) for node in tree]
     )
 
 
 def _build_tree_response(category: TagCategory) -> dict:
-    """TagCategory 转换为完整的树响应结"""
+    """TagCategory 转换为完整的树响应结"""
     return {
         'id': category.id,
         'parent_id': category.parent_id,
@@ -175,13 +175,13 @@ async def get_category_breadcrumb(
     category_id: int = Path(..., description="分类ID"),
     session: AsyncSession = Depends(get_db)
 ):
-    """获取分类的面包屑路径"""
+    """获取分类的路径"""
     service = TagCategoryService(session)
     breadcrumb = await service.get_breadcrumb(category_id)
     
     return ResponseSchema(
-        code=200,
-        message="获取面包屑路径成功",
+        code="000000",
+        message="获取成功",
         data=breadcrumb
     )
 
@@ -189,7 +189,7 @@ async def get_category_breadcrumb(
 @router.post("/batch/delete", response_model=ResponseSchema)
 async def batch_delete_categories(
     request: TagCategoryBatchDeleteRequest,
-    soft_delete: bool = Query(True, description="是否删除"),
+    soft_delete: bool = Query(True, description="是否删除"),
     session: AsyncSession = Depends(get_db)
 ):
     """批量删除标签分类"""
@@ -200,7 +200,7 @@ async def batch_delete_categories(
     await session.commit()
     
     return ResponseSchema(
-        code=200,
+        code="000000",
         message=message,
         data={"success_count": success_count, "total": len(request.ids)}
     )
@@ -218,7 +218,7 @@ async def move_tag_category(
     await session.commit()
     
     return ResponseSchema(
-        code=200,
+        code="000000",
         message="移动分类成功",
         data=result.model_dump()
     )
@@ -227,7 +227,7 @@ async def move_tag_category(
 @router.post("/reorder/{category_id}", response_model=ResponseSchema)
 async def reorder_tag_category(
     category_id: int = Path(..., description="分类ID"),
-    sort_no: int = Query(..., description="新的排序"),
+    sort_no: int = Query(..., description="新的排序"),
     session: AsyncSession = Depends(get_db)
 ):
     """重新排序分类"""
@@ -236,7 +236,7 @@ async def reorder_tag_category(
     await session.commit()
     
     return ResponseSchema(
-        code=200,
+        code="000000",
         message="排序成功",
         data=result.model_dump()
     )
@@ -245,7 +245,7 @@ async def reorder_tag_category(
 @router.get("/children-count/{category_id}", response_model=ResponseSchema)
 async def get_children_count(
     category_id: int = Path(..., description="分类ID"),
-    recursive: bool = Query(False, description="是否递归计算所有后"),
+    recursive: bool = Query(False, description="是否递归计算所有后"),
     session: AsyncSession = Depends(get_db)
 ):
     """获取分类的子分类数量"""
@@ -253,7 +253,7 @@ async def get_children_count(
     count = await service.get_children_count(category_id, recursive=recursive)
     
     return ResponseSchema(
-        code=200,
+        code="000000",
         message="获取子分类数量成功",
         data={"count": count}
     )

+ 669 - 0
项目/API接口定义/字典管理API接口.md

@@ -0,0 +1,669 @@
+# 字典管理系统API接口文档
+
+## 概述
+字典管理系统提供字典类型和字典项的管理功能,支持树形结构展示、增删改查、批量操作等功能。
+
+## 基础信息
+- 基础路径: `/api/v1`
+- 认证方式: Bearer Token
+- 响应格式: JSON
+
+## 通用响应格式
+```json
+{
+  "code": "000000",
+  "message": "成功",
+  "data": {},
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+## 错误码说明
+- `000000`: 成功
+- `100001`: 参数验证失败
+- `200002`: 令牌失效
+- `300001`: 资源不存在
+- `400001`: 业务逻辑错误
+- `500001`: 服务器内部错误
+
+---
+
+## 字典类型管理接口
+
+### 1. 获取字典类型树形结构
+获取所有字典类型的树形结构,用于左侧树形展示。
+
+**接口地址**: `GET /api/v1/dict/category/tree`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+```
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "成功",
+  "data": [
+    {
+      "category_id": "file_type",
+      "category_name": "文件类型枚举定义",
+      "parent_field": "0",
+      "children": []
+    },
+    {
+      "category_id": "professional_type",
+      "category_name": "专业类型枚举定义",
+      "parent_field": "0",
+      "children": []
+    }
+  ],
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 2. 获取字典类型列表(分页)
+获取字典类型列表,支持关键字搜索和分页。
+
+**接口地址**: `POST /api/v1/dict/category/list`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+Content-Type: application/json
+```
+
+**请求参数**:
+```json
+{
+  "keyword": "文件",
+  "page": 1,
+  "page_size": 10
+}
+```
+
+**参数说明**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| keyword | string | 否 | 关键字搜索(搜索类型名称和编号) |
+| page | int | 是 | 页码,从1开始 |
+| page_size | int | 是 | 每页数量,1-100 |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "成功",
+  "data": {
+    "list": [
+      {
+        "category_id": "file_type",
+        "category_no": "file_type",
+        "category_name": "文件类型枚举定义",
+        "category_desc": "文件类型枚举定义",
+        "parent_field": "0",
+        "category_level": "1",
+        "del_flag": "0",
+        "created_by": "user_id",
+        "created_time": "2026-02-22T10:00:00",
+        "updated_by": "user_id",
+        "updated_time": "2026-02-22T10:00:00",
+        "created_by_name": "管理员",
+        "updated_by_name": "管理员"
+      }
+    ],
+    "total": 8,
+    "page": 1,
+    "page_size": 10
+  },
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 3. 获取字典类型详情
+根据ID获取字典类型详细信息。
+
+**接口地址**: `GET /api/v1/dict/category/{category_id}`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+```
+
+**路径参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| category_id | string | 是 | 字典类型ID |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "成功",
+  "data": {
+    "category_id": "file_type",
+    "category_no": "file_type",
+    "category_name": "文件类型枚举定义",
+    "category_desc": "文件类型枚举定义",
+    "parent_field": "0",
+    "category_level": "1",
+    "del_flag": "0",
+    "created_by": "user_id",
+    "created_time": "2026-02-22T10:00:00",
+    "updated_by": "user_id",
+    "updated_time": "2026-02-22T10:00:00",
+    "created_by_name": "管理员",
+    "updated_by_name": "管理员"
+  },
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 4. 创建字典类型
+创建新的字典类型。
+
+**接口地址**: `POST /api/v1/dict/category`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+Content-Type: application/json
+```
+
+**请求参数**:
+```json
+{
+  "category_no": "custom_type",
+  "category_name": "自定义类型",
+  "category_desc": "自定义类型说明",
+  "parent_field": "0",
+  "category_level": "1"
+}
+```
+
+**参数说明**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| category_no | string | 否 | 字典类型编号,最大512字符 |
+| category_name | string | 是 | 字典类型名称,最大512字符 |
+| category_desc | string | 否 | 字典类型备注,最大512字符 |
+| parent_field | string | 否 | 父节点ID,默认"0"表示根节点 |
+| category_level | string | 否 | 字典层级 |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "字典类型创建成功",
+  "data": {
+    "category_id": "uuid-generated-id"
+  },
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 5. 更新字典类型
+更新字典类型信息。
+
+**接口地址**: `PUT /api/v1/dict/category/{category_id}`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+Content-Type: application/json
+```
+
+**路径参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| category_id | string | 是 | 字典类型ID |
+
+**请求参数**:
+```json
+{
+  "category_name": "更新后的类型名称",
+  "category_desc": "更新后的说明"
+}
+```
+
+**参数说明**: 所有字段均为可选,只更新提供的字段
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "字典类型更新成功",
+  "data": null,
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 6. 删除字典类型
+删除字典类型(逻辑删除)。
+
+**接口地址**: `DELETE /api/v1/dict/category/{category_id}`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+```
+
+**路径参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| category_id | string | 是 | 字典类型ID |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "字典类型删除成功",
+  "data": null,
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+**注意事项**:
+- 如果字典类型下有子节点,无法删除
+- 如果字典类型下有字典项,无法删除
+
+---
+
+## 字典项管理接口
+
+### 1. 获取字典项列表(分页)
+获取字典项列表,支持按字典类型、关键字、启用状态筛选。
+
+**接口地址**: `POST /api/v1/dict/item/list`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+Content-Type: application/json
+```
+
+**请求参数**:
+```json
+{
+  "category_id": "file_type",
+  "keyword": "标准",
+  "enable_flag": "1",
+  "page": 1,
+  "page_size": 10
+}
+```
+
+**参数说明**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| category_id | string | 否 | 字典类型ID |
+| keyword | string | 否 | 关键字搜索(搜索名称、值、备注) |
+| enable_flag | string | 否 | 启用状态:1启用,0禁用 |
+| page | int | 是 | 页码,从1开始 |
+| page_size | int | 是 | 每页数量,1-100 |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "成功",
+  "data": {
+    "list": [
+      {
+        "dict_id": 1,
+        "dict_name": "国家标准",
+        "dict_value": "GB",
+        "dict_desc": "National Standard",
+        "category_id": "file_type",
+        "category_name": "文件类型枚举定义",
+        "enable_flag": "1",
+        "del_flag": "0",
+        "sort": 1,
+        "created_by": "user_id",
+        "created_time": "2026-02-22T10:00:00",
+        "updated_by": "user_id",
+        "updated_time": "2026-02-22T10:00:00",
+        "created_by_name": "管理员",
+        "updated_by_name": "管理员"
+      }
+    ],
+    "total": 12,
+    "page": 1,
+    "page_size": 10
+  },
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 2. 获取字典项详情
+根据ID获取字典项详细信息。
+
+**接口地址**: `GET /api/v1/dict/item/{dict_id}`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+```
+
+**路径参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| dict_id | int | 是 | 字典项ID |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "成功",
+  "data": {
+    "dict_id": 1,
+    "dict_name": "国家标准",
+    "dict_value": "GB",
+    "dict_desc": "National Standard",
+    "category_id": "file_type",
+    "category_name": "文件类型枚举定义",
+    "enable_flag": "1",
+    "del_flag": "0",
+    "sort": 1,
+    "created_by": "user_id",
+    "created_time": "2026-02-22T10:00:00",
+    "updated_by": "user_id",
+    "updated_time": "2026-02-22T10:00:00",
+    "created_by_name": "管理员",
+    "updated_by_name": "管理员"
+  },
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 3. 创建字典项
+创建新的字典项。
+
+**接口地址**: `POST /api/v1/dict/item`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+Content-Type: application/json
+```
+
+**请求参数**:
+```json
+{
+  "dict_name": "自定义项",
+  "dict_value": "CUSTOM",
+  "dict_desc": "Custom Item",
+  "category_id": "file_type",
+  "enable_flag": "1",
+  "sort": 100
+}
+```
+
+**参数说明**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| dict_name | string | 是 | 字典名称,最大512字符 |
+| dict_value | string | 是 | 字典值,最大512字符 |
+| dict_desc | string | 否 | 字典备注,最大512字符 |
+| category_id | string | 是 | 字典类型ID |
+| enable_flag | string | 否 | 启用标志:1启用,0禁用,默认1 |
+| sort | int | 否 | 排序值 |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "字典项创建成功",
+  "data": {
+    "dict_id": 100
+  },
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 4. 更新字典项
+更新字典项信息。
+
+**接口地址**: `PUT /api/v1/dict/item/{dict_id}`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+Content-Type: application/json
+```
+
+**路径参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| dict_id | int | 是 | 字典项ID |
+
+**请求参数**:
+```json
+{
+  "dict_name": "更新后的名称",
+  "dict_desc": "更新后的备注",
+  "sort": 50
+}
+```
+
+**参数说明**: 所有字段均为可选,只更新提供的字段
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "字典项更新成功",
+  "data": null,
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 5. 删除字典项
+删除字典项(逻辑删除)。
+
+**接口地址**: `DELETE /api/v1/dict/item/{dict_id}`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+```
+
+**路径参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| dict_id | int | 是 | 字典项ID |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "字典项删除成功",
+  "data": null,
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 6. 批量删除字典项
+批量删除多个字典项(逻辑删除)。
+
+**接口地址**: `POST /api/v1/dict/item/batch-delete`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+Content-Type: application/json
+```
+
+**请求参数**:
+```json
+{
+  "dict_ids": [1, 2, 3, 4, 5]
+}
+```
+
+**参数说明**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| dict_ids | array | 是 | 字典项ID列表 |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "成功删除5个字典项",
+  "data": null,
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+### 7. 切换字典项启用状态
+启用或禁用字典项。
+
+**接口地址**: `PUT /api/v1/dict/item/{dict_id}/toggle-status?enable_flag={flag}`
+
+**请求头**:
+```
+Authorization: Bearer {token}
+```
+
+**路径参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| dict_id | int | 是 | 字典项ID |
+
+**查询参数**:
+| 参数 | 类型 | 必填 | 说明 |
+|------|------|------|------|
+| enable_flag | string | 是 | 启用标志:1启用,0禁用 |
+
+**响应示例**:
+```json
+{
+  "code": "000000",
+  "message": "字典项启用成功",
+  "data": null,
+  "timestamp": "2026-02-22T10:00:00.000Z"
+}
+```
+
+---
+
+## 使用示例
+
+### Python示例
+```python
+import requests
+
+# 基础配置
+BASE_URL = "http://localhost:8000/api/v1"
+TOKEN = "your_access_token"
+
+headers = {
+    "Authorization": f"Bearer {TOKEN}",
+    "Content-Type": "application/json"
+}
+
+# 1. 获取字典类型树
+response = requests.get(f"{BASE_URL}/dict/category/tree", headers=headers)
+print(response.json())
+
+# 2. 获取字典项列表
+data = {
+    "category_id": "file_type",
+    "page": 1,
+    "page_size": 10
+}
+response = requests.post(f"{BASE_URL}/dict/item/list", headers=headers, json=data)
+print(response.json())
+
+# 3. 创建字典项
+data = {
+    "dict_name": "测试项",
+    "dict_value": "TEST",
+    "dict_desc": "Test Item",
+    "category_id": "file_type",
+    "enable_flag": "1",
+    "sort": 100
+}
+response = requests.post(f"{BASE_URL}/dict/item", headers=headers, json=data)
+print(response.json())
+```
+
+### JavaScript示例
+```javascript
+const BASE_URL = "http://localhost:8000/api/v1";
+const TOKEN = "your_access_token";
+
+const headers = {
+    "Authorization": `Bearer ${TOKEN}`,
+    "Content-Type": "application/json"
+};
+
+// 1. 获取字典类型树
+fetch(`${BASE_URL}/dict/category/tree`, { headers })
+    .then(res => res.json())
+    .then(data => console.log(data));
+
+// 2. 获取字典项列表
+fetch(`${BASE_URL}/dict/item/list`, {
+    method: "POST",
+    headers,
+    body: JSON.stringify({
+        category_id: "file_type",
+        page: 1,
+        page_size: 10
+    })
+})
+    .then(res => res.json())
+    .then(data => console.log(data));
+
+// 3. 创建字典项
+fetch(`${BASE_URL}/dict/item`, {
+    method: "POST",
+    headers,
+    body: JSON.stringify({
+        dict_name: "测试项",
+        dict_value: "TEST",
+        dict_desc: "Test Item",
+        category_id: "file_type",
+        enable_flag: "1",
+        sort: 100
+    })
+})
+    .then(res => res.json())
+    .then(data => console.log(data));
+```
+
+---
+
+## 注意事项
+
+1. 所有接口都需要Bearer Token认证
+2. 字典类型ID使用字符串类型,字典项ID使用整数类型
+3. 删除操作均为逻辑删除,不会物理删除数据
+4. 创建人、修改人、创建时间、修改时间由系统自动维护
+5. 字典项按sort字段升序排序,sort相同时按dict_id升序
+6. 字段长度限制需严格遵守,避免数据库异常

+ 315 - 0
项目/字典管理系统设计.md

@@ -0,0 +1,315 @@
+
+
+
+
+### 系统字典管理设计
+
+#### 系统字典表结构
+
+- 字典类型表:t_sys_dict_category
+DROP TABLE IF EXISTS `t_sys_dict_category`;
+CREATE TABLE `t_sys_dict_category`  (
+  `category_id` varchar(32)  NOT NULL COMMENT '字典类型id',
+  `category_no` varchar(512)  DEFAULT NULL COMMENT '字典类型编号',
+  `category_name` varchar(512) NOT NULL COMMENT '字典类型种类id',
+  `category_desc` varchar(512)  DEFAULT NULL COMMENT '字典类型种类备注',
+  `parent_field` varchar(32)  NOT NULL COMMENT '父节点字典类型种类id',
+  `created_by` varchar(32)  DEFAULT NULL COMMENT '创建人',
+  `created_time` datetime DEFAULT NULL COMMENT '创建时间',
+  `updated_by` varchar(32)  DEFAULT NULL COMMENT '修改人',
+  `updated_time` datetime DEFAULT NULL COMMENT '修改时间',
+  `del_flag` char(1) DEFAULT '0' COMMENT '删除标志位:1是0否',
+  `category_level` char(1) DEFAULT NULL COMMENT '字典层级',
+  PRIMARY KEY (`category_id`) USING BTREE
+) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_bin COMMENT = '字典类别表';
+
+
+
+- 字典项表:t_sys_dictionary_item
+DROP TABLE IF EXISTS `t_sys_dict_category_item`;
+CREATE TABLE `t_sys_dict_category_item`  (
+  `dict_id` int NOT NULL AUTO_INCREMENT COMMENT '字典id',
+  `dict_name` varchar(512) NOT NULL COMMENT '字典名称',
+  `dict_value` varchar(512) NOT NULL COMMENT '字典值',
+  `dict_desc` varchar(512) DEFAULT NULL COMMENT '字典备注',
+  `category_id` varchar(255)  NOT NULL COMMENT '字典类型id',
+  `created_by` varchar(32)  DEFAULT NULL COMMENT '创建人',
+  `created_time` datetime COMMENT '创建时间',
+  `updated_by` varchar(32) DEFAULT NULL COMMENT '修改人',
+  `updated_time` datetime  DEFAULT NULL COMMENT '修改时间',
+  `enable_flag` char(1) NOT NULL DEFAULT '1' COMMENT '启用标志位:1启用0禁用',
+  `del_flag` char(1) NOT NULL DEFAULT '0' COMMENT '删除标志位:1是0否',
+  `sort` int  COMMENT '排序',
+  PRIMARY KEY (`dict_id`) USING BTREE
+) ENGINE = InnoDB AUTO_INCREMENT = 1 COMMENT = '字典项表';
+
+
+#### 系统字典逻辑
+ - 首先创建字典类型
+   - 创建字典类型时,字典项的category_id字段,为字典类型id
+ - 再创建字典项
+   - 字典项的category_id字段,为字典类型id
+   - 字典项的dict_id字段,为字典项id
+  
+#### 原型UI界面设计
+ - 字典类型管理
+   - 左侧树形型结构
+   - 可以增加字典类型
+   - 可以删除字典类型
+   - 可以修改字典类型
+   - 可以查看字典类型
+   - 选择树形节点后,右侧显示该节点下的字典项
+ - 字典项管理
+   - 右侧列表显示字典项信息
+   - 可以增加字典项
+   - 可以删除字典项
+   - 可以修改字典项
+   - 可以查看字典项
+   - 可以搜索字典项
+   - 可以排序字典项
+   - 可以批量删除字典项
+   - 可以启用、禁用字典项
+   - 可以设置字典项的排序
+ 
+
+#### 目录结构
+  - 字典类型管理
+    - src/views/system/dict_category_view.py
+    - src/schemas/system/dict_category_schema.py
+    - src/services/system/dict_category_service.py
+
+  - 字典项管理
+    - src/views/system/dict_item_view.py
+    - src/schemas/system/dict_item_schema.py
+    - src/services/system/dict_item_service.py
+  
+#### 初始化数据
+ - 创建字典类型
+    文件类型枚举定义  file_type
+    专业类型枚举定义  professional_type
+    时效性枚举定义    time_effect
+    施工方案类别枚举定义    construction_plan_type
+    施工方案一级分类枚举定义    construction_plan_first_type
+    施工方案二级分类枚举定义    construction_plan_second_type
+    施工方案三级分类枚举定义    construction_plan_third_type
+    施工方案四级分类枚举定义    construction_plan_fourth_type
+
+
+ - 字典项枚举定义
+    - 文件类型枚举定义
+        序号 中文名称(dict_name)	英文名称(dict_desc)	        简写(dict_value)
+        1	国家标准	National Standard	GB	
+        2	行业标准	Industry Standard	HY	
+        3	部门规章	Department Regulations	BM	
+        4	地方标准	Local Standard	DB	
+        5	企业标准	Enterprise Standard	QY	
+        6	管理制度	Management Regulation	GL	
+        7	技术规范	Technical Specification	GF	
+        8	团体标准	Group Standard	TT	
+        9	国际标准	International Standard	GJ	
+        10	国家法律	National Law	FL	
+        11	地方法规	Local Regulations	LR	
+        12	其他	    Other	QT	
+
+    - 专业类型枚举定义
+        序号(sort) 中文名称(dict_name)	英文名称(dict_desc)	        简写(dict_value)
+        1	法律法规	Legal Regulations	FL
+        2	通用标准	General Standards	TY
+        3	勘察钻探	Survey and Drilling	KC
+        4	地基基础	Foundation	DJ
+        5	路基路面	Roadbed and Pavement	LJ
+        6	桥梁工程	Bridge Engineering	QL
+        7	隧道工程	Tunnel Engineering	SD
+        8	交通工程	Traffic Engineering	JT
+        9	建筑工程	Construction Engineering	JZ
+        10	市政工程	Municipal Engineering	SZ
+        11	机电安装	Electromechanical Installation	JD
+        12	路桥工程	Road and Bridge Engineering	LB
+        13	装饰装修	Decoration and Renovation	ZS
+        14	港口航道	Port and Waterway	GK
+        15	铁路工程	Railway Engineering	TL
+        16	房建工程	Building Engineering	FJ
+        17	水利电力	Water Conservancy and Power	SL
+        18	信息化	Information Technology	XX
+        19	试验检测	Testing and Inspection	SY
+        20	安全环保	Safety and Environmental Protection	AQ
+        21	其他	Other	QT
+    - 时效性枚举定义
+        序号 中文描述(dict_name)	英文描述(dict_desc)	        简写(dict_value)
+        1	现行	Current	XH
+        2	废止	Abolished	FZ
+        3	试行	Trial / Interim	SX
+    - 施工程类别枚举定义
+        序号 中文描述(dict_name)	英文描述(dict_desc)	        简写(dict_value)
+        1	超危大方案	Ultra-High-Risk Construction Plan	CH
+        2	超危大方案较大Ⅱ级	Ultra-High-Risk Plan – Level II (Major)	CH2
+        3	超危大方案特大Ⅳ级	Ultra-High-Risk Plan – Level IV (Extra Large)	CH4
+        4	超危大方案一般Ⅰ级	Ultra-High-Risk Plan – Level I (General)	CH1
+        5	超危大方案重大Ⅲ级	Ultra-High-Risk Plan – Level III (Significant)	CH3
+        6	危大方案	High-Risk Construction Plan	WD
+        7	一般方案	General Construction Plan	YB
+        8	其他	Other	QT
+
+    - 施工计划一级分类枚举定义
+        序号 中文描述(dict_name)	英文描述(dict_desc)	        简写(dict_value)
+        1	施工方案	Construction Plan	SC
+        2	其他	Other	QT
+
+    - 施工计划二级分类枚举定义
+        序号 中文描述(dict_name)	英文描述(dict_desc)	        简写(dict_value)
+        1	临建工程	Temporary Construction	LZ
+        2	路基工程	Subgrade Engineering	LJ
+        3	桥梁工程	Bridge Engineering	QL
+        4	隧道工程	Tunnel Engineering	SD
+        5	其他	Other	QT
+
+    - 施工计划三级分类枚举定义
+        序号 中文描述(dict_name)	英文描述(dict_desc)	        简写(dict_value)
+        1	TBM施工	TBM Construction	TM
+        2	拌和站安、拆施工	Mixing Plant Installation & Dismantling	BH
+        3	不良地质隧道施工	Construction in Poor Geological Tunnel	BL
+        4	常规桥梁	Conventional Bridge	CG
+        5	挡土墙工程类	Retaining Wall Engineering	DT
+        6	辅助坑道施工	Auxiliary Adit Construction	FB
+        7	复杂洞口工程施工	Complex Portal Construction	FD
+        8	钢筋加工场安、拆	Reinforcement Yard Setup & Removal	GG
+        9	钢栈桥施工	Steel Trestle Construction	GZ
+        10	拱桥	Arch Bridge	GH
+        11	涵洞工程类	Culvert Engineering	HD
+        12	滑坡体处理类	Landslide Treatment	HP
+        13	路堤	Embankment	LT
+        14	路堑	Cut Slope	LQ
+        15	其他	Other	QT
+        16	深基坑	Deep Foundation Pit	JK
+        17	隧道总体施工	Overall Tunnel Construction	ZT
+        18	特殊结构隧道	Special Structure Tunnel	TS
+        19	斜拉桥	Cable-Stayed Bridge	XL
+        20	悬索桥	Suspension Bridge	XS
+            其他	Other	QT
+
+    - 施工计划四级分类枚举定义
+        序号 中文描述(dict_name)	英文描述(dict_desc)	        简写(dict_value)
+        1	挡土墙	Retaining Wall	DT
+        2	顶管	Pipe Jacking	DG
+        3	断层破碎带及软弱围岩	Fault Zone & Weak Rock Mass	DL
+        4	钢筋砼箱涵	Reinforced Concrete Box Culvert	GX
+        5	高填路堤	High Fill Embankment	GT
+        6	抗滑桩	Anti-Slide Pile	KH
+        7	软岩大变形隧道	Large Deformation Tunnel in Soft Rock	RY
+        8	上部结构	Superstructure	SB
+        9	深基坑开挖与支护	Deep Excavation & Support	JK
+        10	深挖路堑	Deep Road Cut	LC
+        11	隧道TBM	Tunnel Boring Machine (TBM)	TM
+        12	隧道进洞	Tunnel Portal Entry	JD
+        13	隧道竖井	Tunnel Shaft	SJ
+        14	隧道斜井	Tunnel Incline Shaft	XJ
+        15	特种设备	Special Equipment	TZ
+        16	瓦斯隧道	Gas-Prone Tunnel	WS
+        17	下部结构	Substructure	XB
+        18	小净距隧道	Small Spacing Tunnel	NJ
+        19	岩爆隧道	Rockburst Tunnel	YB
+        20	岩溶隧道	Karst Tunnel	YR
+        21	涌水突泥隧道	Water Inrush & Mud Burst Tunnel	YN
+        22	桩基础	Pile Foundation	ZJ
+        23	其他	Other	QT
+
+
+
+
+
+#### 前端系统设计
+  - 
+
+
+
+
+
+
+### 字典功能开发
+
+#### 字典功能应用开发
+  - 本次调整包括对应功能的 LQAdminPlatform 后端逻辑 和 LQAdminFront 前端逻辑 
+  - 知识库管理
+    - 知识库新增:元数据字段定义,字段类型 新增“字典”选型,用户选择字典类型时,同时新增字段 字典类别为下拉树形选择字典类别
+    - 知识库元数据表 “t_samp_metadata” 新增字段  `category_id` varchar(50)  COMMENT '字典类型id'
+    - 知识库新增元数据表定义,只有用户选择字段类型为“字典” 时,字典类别不能为空 ,必须选择
+
+  - 检索引擎管理 
+    - 检索模式 为 高级模式
+      - 元数据过滤条件  当字段类型为字典类型时,输入为下拉列表(根据知识库元数据表 category_id 字段的值 查询字典项列表 供用户选择),而非输入框,输入框只有字段类型为 文本或数字时
+    
+    - 知识片段管理 列表界面、详情界面
+      - 首先确认 知识片段所属 具体文档,再根据文档对应具体知识库,再根据知识库对应元数据字段信息
+      - 元数据字段信息,如果元数据字段类型为字典类型时,需要转换为字典名称
+       - 根据知识库元数据表 category_id 字段的值和具体字典值 查询字典项表(t_sys_dict_category_item 条件:category_id 、dict_value )的字典名称(dict_name)进行展示
+
+    - 检索模式 列表界面 、详情界面
+      - 首先确认 知识片段所属 具体文档,再根据文档对应具体知识库,再根据知识库对应元数据字段信息
+      - 元数据字段信息,如果元数据字段类型为字典类型时,需要转换为字典名称
+       - 根据知识库元数据表 category_id 字段的值和具体字典值 查询字典项表(t_sys_dict_category_item 条件:category_id 、dict_value )的字典名称(dict_name)进行展示
+       
+  - 文档管理中心
+    - 列表编辑 , 每一条记录编辑界面 
+      - 施工标准规范基本信息编辑
+        - 新增编辑字段 ,通过如下信息构建字段的选项列表(下拉显示名称使用字典名称:dict_name, 下拉value值使用字典值:dict_value)
+           - 文件类型 ,使用字典列表的字典项数据  文件类型枚举定义  file_type
+           - 专业领域 ,使用字典列表的字典项数据  专业类型枚举定义  professional_type
+           - 时效性 , 使用字典列表的字典项数据    时效性枚举定义    time_effect
+      - 施工方案基本信息编辑
+         - 新增编辑字段 ,通过如下信息构建字段的选项列表(下拉显示名称使用字典名称:dict_name, 下拉value值使用字典值:dict_value)
+           - 方案类别 ,使用字典列表的字典项数据   施工方案类别枚举定义       construction_plan_type
+           - 一级分类 ,使用字典列表的字典项数据   施工方案一级分类枚举定义    construction_plan_first_type
+           - 二级分类 ,使用字典列表的字典项数据   施工方案二级分类枚举定义    construction_plan_second_type
+           - 三级分类 ,使用字典列表的字典项数据   施工方案三级分类枚举定义    construction_plan_third_type
+           - 四级分类 ,使用字典列表的字典项数据   施工方案四级分类枚举定义    construction_plan_fourth_type
+
+  - 文档基本信息
+     - 施工标准规范管理 
+       - 列表查询筛选 下拉列表使用字典类别及字段项完成下拉列表初始化
+       - 列表数据展示  将列表中的 文件类型、专业领域、时效性 字典值 转换为字典名称
+       - 新增编辑字段 ,通过如下信息构建字段的选项列表(下拉显示名称使用字典名称:dict_name, 下拉value值使用字典值:dict_value)
+           - 文件类型 ,使用字典列表的字典项数据  文件类型枚举定义  file_type
+           - 专业领域 ,使用字典列表的字典项数据  专业类型枚举定义  professional_type
+           - 时效性 , 使用字典列表的字典项数据    时效性枚举定义    time_effect
+
+     - 施工方案管理
+       - 列表查询筛选 下拉列表使用字典类别及字段项完成下拉列表初始化
+       - 列表数据展示  将列表中的 方案类别、一级分类、二级分类、三级分类、四级分类 字典值 转换为字典名称
+       - 新增编辑字段 ,通过如下信息构建字段的选项列表(下拉显示名称使用字典名称:dict_name, 下拉value值使用字典值:dict_value)
+           - 方案类别 ,使用字典列表的字典项数据   施工方案类别枚举定义       construction_plan_type
+           - 一级分类 ,使用字典列表的字典项数据   施工方案一级分类枚举定义    construction_plan_first_type
+           - 二级分类 ,使用字典列表的字典项数据   施工方案二级分类枚举定义    construction_plan_second_type
+           - 三级分类 ,使用字典列表的字典项数据   施工方案三级分类枚举定义    construction_plan_third_type
+           - 四级分类 ,使用字典列表的字典项数据   施工方案四级分类枚举定义    construction_plan_fourth_type
+
+
+
+
+
+#### 字典项功能应用修复
+
+  - 文档基本信息
+     - 施工标准规范管理 
+       - 列表查询筛选 下拉列表使用字典类别及字段项完成下拉列表初始化
+         - 时效性 , 使用字典列表的字典项数据    时效性枚举定义    time_effect
+       - 列表数据展示  将列表中的 文件类型、专业领域、时效性 字典值 转换为字典名称
+       - 新增编辑字段 ,通过如下信息构建字段的选项列表(下拉显示名称使用字典名称:dict_name, 下拉value值使用字典值:dict_value)
+           - 时效性 , 使用字典列表的字典项数据    时效性枚举定义    time_effect
+
+  - 文档管理中心
+    - 列表编辑 , 每一条记录编辑界面 
+      - 施工标准规范基本信息编辑
+        - 新增编辑字段 ,通过如下信息构建字段的选项列表(下拉显示名称使用字典名称:dict_name, 下拉value值使用字典值:dict_value)
+           - 文件类型 ,使用字典列表的字典项数据  文件类型枚举定义  file_type
+           - 专业领域 ,使用字典列表的字典项数据  专业类型枚举定义  professional_type
+           - 时效性 , 使用字典列表的字典项数据    时效性枚举定义    time_effect
+      - 施工方案基本信息编辑
+         - 新增编辑字段 ,通过如下信息构建字段的选项列表(下拉显示名称使用字典名称:dict_name, 下拉value值使用字典值:dict_value)
+           - 方案类别 ,使用字典列表的字典项数据   施工方案类别枚举定义       construction_plan_type
+           - 一级分类 ,使用字典列表的字典项数据   施工方案一级分类枚举定义    construction_plan_first_type
+           - 二级分类 ,使用字典列表的字典项数据   施工方案二级分类枚举定义    construction_plan_second_type
+           - 三级分类 ,使用字典列表的字典项数据   施工方案三级分类枚举定义    construction_plan_third_type
+           - 四级分类 ,使用字典列表的字典项数据   施工方案四级分类枚举定义    construction_plan_fourth_type
+
+   - 知识片段管理
+       - 知识库查询筛选下拉列表,默认为空

Некоторые файлы не были показаны из-за большого количества измененных файлов