【同步】BOOT 和 CLOUD 的功能(oa)

This commit is contained in:
YunaiV
2026-09-19 09:54:09 +08:00
parent 589b75a1f2
commit 15892e0a3e
521 changed files with 54737 additions and 210 deletions
+1
View File
@@ -32,6 +32,7 @@
<module>yudao-module-hrm</module>
<module>yudao-module-fms</module>
<module>yudao-module-pms</module>
<module>yudao-module-oa</module>
</modules>
<name>${project.artifactId}</name>
+147 -207
View File
@@ -11,7 +11,7 @@
Target Server Version : 80200 (8.2.0)
File Encoding : 65001
Date: 30/08/2026 18:00:12
Date: 04/09/2026 08:44:59
*/
SET NAMES utf8mb4;
@@ -49,7 +49,7 @@ CREATE TABLE `infra_api_access_log` (
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_create_time`(`create_time` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 39621 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
) ENGINE = InnoDB AUTO_INCREMENT = 41072 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API 访问日志表';
-- ----------------------------
-- Records of infra_api_access_log
@@ -92,7 +92,7 @@ CREATE TABLE `infra_api_error_log` (
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_create_time`(`create_time` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 26249 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
) ENGINE = InnoDB AUTO_INCREMENT = 26287 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
-- ----------------------------
-- Records of infra_api_error_log
@@ -253,7 +253,7 @@ CREATE TABLE `infra_file` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2280 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
) ENGINE = InnoDB AUTO_INCREMENT = 2295 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
-- ----------------------------
-- Records of infra_file
@@ -284,8 +284,8 @@ CREATE TABLE `infra_file_config` (
-- Records of infra_file_config
-- ----------------------------
BEGIN;
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库(示例)', 1, '我是数据库', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2026-07-02 15:35:07', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, '七牛存储器(示例)', 20, '请换成你自己的密钥!!!', b'1', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true,\"region\":null}', '1', '2024-01-13 22:11:12', '1', '2026-08-19 13:12:48', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库(示例)', 1, '我是数据库', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2026-09-01 16:54:50', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, '七牛存储器(示例)', 20, '请换成你自己的密钥!!!', b'1', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3-cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true,\"region\":null}', '1', '2024-01-13 22:11:12', '1', '2026-09-01 16:54:50', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (24, '腾讯云存储(示例)', 20, '请换成你的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"https://cos.ap-shanghai.myqcloud.com\",\"domain\":\"http://tengxun-oss.iocoder.cn\",\"bucket\":\"aoteman-1255880240\",\"accessKey\":\"AKIDAF6WSh1uiIjwqtrOsGSN3WryqTM6cTMt\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:03:22', '1', '2026-07-02 15:35:07', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (25, '阿里云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"oss-cn-beijing.aliyuncs.com\",\"domain\":\"http://ali-oss.iocoder.cn\",\"bucket\":\"yunai-aoteman\",\"accessKey\":\"LTAI5tEQLgnDyjh3WpNcdMKA\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:47:08', '1', '2026-07-02 15:35:07', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (26, '火山云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"tos-s3-cn-beijing.volces.com\",\"domain\":null,\"bucket\":\"yunai\",\"accessKey\":\"AKLTZjc3Zjc4MzZmMjU3NDk0ZTgxYmIyMmFkNTIwMDI1ZGE\",\"accessSecret\":\"X==\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:56:42', '1', '2026-07-02 15:35:07', b'0');
@@ -313,7 +313,7 @@ CREATE TABLE `infra_file_content` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_config_id_path`(`config_id` ASC, `path` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 288 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
) ENGINE = InnoDB AUTO_INCREMENT = 303 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
-- ----------------------------
-- Records of infra_file_content
@@ -341,7 +341,7 @@ CREATE TABLE `infra_job` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 12906 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
) ENGINE = InnoDB AUTO_INCREMENT = 12907 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '定时任务表';
-- ----------------------------
-- Records of infra_job
@@ -373,6 +373,7 @@ INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12903, 'HRM 员工生命周期生效', 1, 'hrmEmployeeChangeJob', '', '0 5 0 * * ?', 3, 60000, 1800000, '1', '2026-08-02 21:16:44', '1', '2026-08-02 21:16:44', b'0');
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12904, 'CMS 静态化任务兜底 Job', 1, 'cmsStaticTaskJob', '', '0 * * * * ?', 0, 0, 0, 'admin', '2026-08-19 23:06:46', 'admin', '2026-08-19 23:06:46', b'0');
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12905, 'CMS 每日全站静态化 Job', 1, 'cmsStaticTaskFullGenerateJob', '', '0 0 2 * * ?', 0, 0, 0, 'admin', '2026-08-19 23:06:46', 'admin', '2026-08-19 23:06:46', b'0');
INSERT INTO `infra_job` (`id`, `name`, `status`, `handler_name`, `handler_param`, `cron_expression`, `retry_count`, `retry_interval`, `monitor_timeout`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12906, 'PMS 知识库回收站清理 Job', 1, 'pmsKnowledgeRecycleCleanJob', '', '0 0 2 * * ?', 3, 60000, 0, '1', '2026-08-31 22:15:54', '1', '2026-08-31 22:15:54', b'0');
COMMIT;
-- ----------------------------
@@ -470,7 +471,7 @@ CREATE TABLE `system_dict_data` (
`update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1337 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
) ENGINE = InnoDB AUTO_INCREMENT = 1409 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
-- ----------------------------
-- Records of system_dict_data
@@ -1784,6 +1785,64 @@ INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `st
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1334, 1, '无', '0', 'pms_work_item_priority', 0, 'default', '', '无', '1', '2026-08-29 17:29:01', '1', '2026-08-29 17:29:01', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1335, 15, '支付宝小程序支付', 'alipay_lite', 'pay_channel_code', 0, 'primary', '', '支付宝小程序支付', '1', '2026-08-30 11:54:48', '1', '2026-08-30 11:54:48', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1336, 7, 'TCP', 'tcp', 'iot_protocol_type', 0, '', '', 'TCP 协议', '1', '2026-08-30 16:07:20', '1', '2026-08-30 16:07:20', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1337, 1, '是', 'true', 'cms_boolean', 0, 'success', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1338, 2, '否', 'false', 'cms_boolean', 0, 'info', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1339, 1, '独立访客', '1', 'cms_interaction_mode', 0, '', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1340, 2, '独立 IP', '2', 'cms_interaction_mode', 0, '', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1341, 3, '独立用户', '3', 'cms_interaction_mode', 0, '', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1342, 0, '正常', '0', 'cms_vote_status', 0, 'success', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1343, 1, '未启用', '1', 'cms_vote_status', 0, 'info', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1344, 2, '未开始', '2', 'cms_vote_status', 0, 'warning', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1345, 3, '已结束', '3', 'cms_vote_status', 0, 'danger', '', '', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1346, 1, '通用项目', '1', 'pms_project_type', 0, 'primary', '', '通用项目', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1347, 2, '敏捷开发项目', '2', 'pms_project_type', 0, 'success', '', '敏捷开发项目', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1348, 1, '进行中', '1', 'pms_project_status', 0, 'primary', '', '进行中', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1349, 2, '已归档', '2', 'pms_project_status', 0, 'info', '', '已归档', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1350, 3, '回收站', '3', 'pms_project_status', 0, 'danger', '', '回收站', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1351, 1, '最高', '1', 'pms_project_level', 0, 'danger', '', '最高', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1352, 2, '较高', '2', 'pms_project_level', 0, 'warning', '', '较高', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1353, 3, '普通', '3', 'pms_project_level', 0, 'primary', '', '普通', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1354, 4, '较低', '4', 'pms_project_level', 0, 'info', '', '较低', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1355, 5, '最低', '5', 'pms_project_level', 0, 'info', '', '最低', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1356, 1, '全部项目', '1', 'pms_project_group_type', 0, 'primary', '', '全部项目', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1357, 2, '未分组', '2', 'pms_project_group_type', 0, 'info', '', '未分组', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1358, 3, '自定义分组', '3', 'pms_project_group_type', 0, 'success', '', '自定义分组', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1359, 1, '全部项目', '1', 'pms_project_scene_type', 0, 'primary', '', '全部项目', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1360, 2, '我负责的', '2', 'pms_project_scene_type', 0, 'success', '', '我负责的', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1361, 3, '我参与的', '3', 'pms_project_scene_type', 0, 'info', '', '我参与的', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1362, 1, '按访问时间', '1', 'pms_project_sort_type', 0, 'primary', '', '按访问时间', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1363, 2, '按创建时间', '2', 'pms_project_sort_type', 0, 'info', '', '按创建时间', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1364, 1, '项目拥有者', '1', 'pms_project_member_level', 0, 'danger', '', '项目拥有者', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1365, 2, '项目管理员', '2', 'pms_project_member_level', 0, 'warning', '', '项目管理员', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1366, 3, '编辑', '3', 'pms_project_member_level', 0, 'primary', '', '编辑', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1367, 4, '只读', '4', 'pms_project_member_level', 0, 'info', '', '只读', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1368, 1, '未开始', '1', 'pms_iteration_status', 0, 'info', '', '未开始', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1369, 2, '进行中', '2', 'pms_iteration_status', 0, 'primary', '', '进行中', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1370, 3, '已完成', '3', 'pms_iteration_status', 0, 'success', '', '已完成', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1371, 1, '需求', '2', 'pms_work_item_type', 0, 'primary', '', '需求', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1372, 2, '任务', '3', 'pms_work_item_type', 0, 'warning', '', '任务', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1373, 3, '缺陷', '4', 'pms_work_item_type', 0, 'danger', '', '缺陷', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1374, 1, '未开始', '1', 'pms_work_item_status_type', 0, 'info', '', '未开始', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1375, 2, '进行中', '2', 'pms_work_item_status_type', 0, 'primary', '', '进行中', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1376, 3, '已完成', '3', 'pms_work_item_status_type', 0, 'success', '', '已完成', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1377, 1, '正常', '1', 'pms_work_item_lifecycle_status', 0, 'primary', '', '正常', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1378, 2, '已归档', '2', 'pms_work_item_lifecycle_status', 0, 'info', '', '已归档', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1379, 3, '回收站', '3', 'pms_work_item_lifecycle_status', 0, 'danger', '', '回收站', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1380, 1, '回收站', '-1', 'pms_knowledge_document_status', 0, 'danger', '', '回收站', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1381, 2, '草稿', '0', 'pms_knowledge_document_status', 0, 'info', '', '草稿', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1382, 3, '正常', '1', 'pms_knowledge_document_status', 0, 'success', '', '正常', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1383, 4, '模板', '2', 'pms_knowledge_document_status', 0, 'primary', '', '模板', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1384, 1, '管理员', '1', 'pms_knowledge_content_level', 0, 'danger', '', '管理员', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1385, 2, '可编辑', '2', 'pms_knowledge_content_level', 0, 'primary', '', '可编辑', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1386, 3, '仅预览', '3', 'pms_knowledge_content_level', 0, 'info', '', '仅预览', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1387, 4, '可下载', '4', 'pms_knowledge_content_level', 0, 'warning', '', '可下载', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1388, 5, '可上传下载', '5', 'pms_knowledge_content_level', 0, 'success', '', '可上传下载', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1389, 1, '全部知识库', '1', 'pms_knowledge_group_type', 0, 'primary', '', '全部知识库', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1390, 2, '未分组', '2', 'pms_knowledge_group_type', 0, 'info', '', '未分组', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1391, 3, '自定义分组', '3', 'pms_knowledge_group_type', 0, 'success', '', '自定义分组', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1392, 1, '创建人', '1', 'pms_knowledge_library_member_level', 0, 'danger', '', '创建人', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1393, 2, '管理员', '2', 'pms_knowledge_library_member_level', 0, 'warning', '', '管理员', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1394, 3, '成员', '3', 'pms_knowledge_library_member_level', 0, 'primary', '', '成员', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0');
COMMIT;
-- ----------------------------
@@ -1803,7 +1862,7 @@ CREATE TABLE `system_dict_type` (
`deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
`deleted_time` datetime NULL DEFAULT NULL COMMENT '删除时间',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 280 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
) ENGINE = InnoDB AUTO_INCREMENT = 298 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
-- ----------------------------
-- Records of system_dict_type
@@ -2083,6 +2142,24 @@ INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creat
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (277, 'PMS 知识对象类型', 'pms_knowledge_object_type', 0, 'PMS 知识对象类型', '1', '2026-08-25 12:17:24', '1', '2026-08-25 12:17:24', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (278, 'PMS 工作项优先级', 'pms_work_item_priority', 0, 'PMS 工作项优先级', '1', '2026-08-29 17:15:11', '1', '2026-08-29 17:15:11', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (279, 'PMS 工作项缺陷类型', 'pms_work_item_defect_type', 0, 'PMS 工作项缺陷类型', '1', '2026-08-29 17:15:11', '1', '2026-08-29 17:15:11', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (280, 'CMS 布尔值', 'cms_boolean', 0, 'CMS 布尔值(true=是,false=否)', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (281, 'CMS 互动参与模式', 'cms_interaction_mode', 0, 'CMS 互动参与模式(1=独立访客,2=独立 IP,3=独立用户)', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (282, 'CMS 投票状态', 'cms_vote_status', 0, 'CMS 投票状态(对齐后端 CmsVoteStatusEnum)', '1', '2026-09-01 21:26:46', '1', '2026-09-01 21:26:46', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (283, 'PMS 项目类型', 'pms_project_type', 0, 'PMS 项目类型', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (284, 'PMS 项目状态', 'pms_project_status', 0, 'PMS 项目状态', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (285, 'PMS 项目优先级', 'pms_project_level', 0, 'PMS 项目优先级', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (286, 'PMS 项目分组类型', 'pms_project_group_type', 0, 'PMS 项目分组类型', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (287, 'PMS 项目列表场景', 'pms_project_scene_type', 0, 'PMS 项目列表场景', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (288, 'PMS 项目排序类型', 'pms_project_sort_type', 0, 'PMS 项目排序类型', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (289, 'PMS 项目成员权限级别', 'pms_project_member_level', 0, 'PMS 项目成员权限级别', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (290, 'PMS 迭代状态', 'pms_iteration_status', 0, 'PMS 迭代状态', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (291, 'PMS 工作项类型', 'pms_work_item_type', 0, 'PMS 工作项类型', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (292, 'PMS 工作项语义状态', 'pms_work_item_status_type', 0, 'PMS 工作项语义状态', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (293, 'PMS 工作项生命周期状态', 'pms_work_item_lifecycle_status', 0, 'PMS 工作项生命周期状态', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (294, 'PMS 知识文档状态', 'pms_knowledge_document_status', 0, 'PMS 知识文档状态', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (295, 'PMS 知识内容协作等级', 'pms_knowledge_content_level', 0, 'PMS 知识内容协作等级', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (296, 'PMS 知识库分组类型', 'pms_knowledge_group_type', 0, 'PMS 知识库分组类型', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (297, 'PMS 知识库成员等级', 'pms_knowledge_library_member_level', 0, 'PMS 知识库成员等级', '1', '2026-09-03 08:49:27', '1', '2026-09-03 08:49:27', b'0', NULL);
COMMIT;
-- ----------------------------
@@ -2108,7 +2185,7 @@ CREATE TABLE `system_login_log` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_username`(`username` ASC) USING BTREE,
INDEX `idx_create_time`(`create_time` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 7387 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
) ENGINE = InnoDB AUTO_INCREMENT = 7678 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
-- ----------------------------
-- Records of system_login_log
@@ -2247,8 +2324,8 @@ CREATE TABLE `system_menu` (
-- Records of system_menu
-- ----------------------------
BEGIN;
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '系统管理', '', 1, 10, 0, '/system', 'ep:tools', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2, '基础设施', '', 1, 20, 0, '/infra', 'ep:monitor', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3, 'OA 示例', '', 1, 40, 148, 'oa', 'fa:road', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2021-09-20 16:26:19', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '用户管理', 'system:user:list', 2, 1, 1, 'user', 'ep:avatar', 'system/user/index', 'SystemUser', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5, '角色管理', '', 2, 2, 1, 'role', 'ep:user', 'system/role/index', 'SystemRole', 0, b'1', b'1', b'1', 'admin', '2021-01-05 17:03:48', '1', '2026-08-14 01:49:42', b'0');
@@ -2360,7 +2437,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (111, '短信日志', '', 2, 2, 98, 'sms-log', 'fa:edit', 'system/sms/log/index', 'SystemSmsLog', 0, b'1', b'1', b'1', '', '2021-04-11 08:37:05', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (112, '短信日志查询', 'system:sms-log:query', 3, 1, 111, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-11 08:37:05', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (113, '短信日志导出', 'system:sms-log:export', 3, 5, 111, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-04-11 08:37:05', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (114, '支付管理', '', 1, 30, 0, '/pay', 'ep:money', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-25 16:43:41', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (114, '支付管理', '', 1, 30, 0, '/pay', 'ep:money', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-25 16:43:41', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (115, '请假查询', '', 2, 0, 3, 'leave', 'fa:leanpub', 'bpm/oa/leave/index', 'BpmOALeave', 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (116, '请假申请查询', 'bpm:oa-leave:query', 3, 1, 115, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (117, '请假申请创建', 'bpm:oa-leave:create', 3, 2, 115, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-09-20 08:51:03', '1', '2026-08-14 01:49:42', b'0');
@@ -2388,7 +2465,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (142, '支付订单', '', 2, 2, 114, 'order', 'fa:cc-paypal', 'pay/order/index', 'PayOrder', 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (143, '支付订单查询', 'pay:order:query', 3, 1, 142, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (147, '支付订单导出', 'pay:order:export', 3, 5, 142, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-25 08:49:43', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (148, '工作流程', '', 1, 50, 0, '/bpm', 'fa:medium', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-30 20:26:36', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (148, '工作流程', '', 1, 50, 0, '/bpm', 'fa:medium', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-30 20:26:36', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (149, '流程管理', '', 1, 10, 148, 'manager', 'fa:dedent', NULL, NULL, 0, b'1', b'1', b'1', '1', '2021-12-30 20:28:30', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (150, '流程表单', '', 2, 2, 149, 'form', 'fa:hdd-o', 'bpm/form/index', 'BpmForm', 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (151, '表单查询', 'bpm:form:query', 3, 1, 150, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2021-12-30 12:38:22', '1', '2026-08-14 01:49:42', b'0');
@@ -2433,7 +2510,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (191, '文件配置删除', 'infra:file-config:delete', 3, 4, 187, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (192, '文件配置导出', 'infra:file-config:export', 3, 5, 187, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-03-15 14:35:28', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (193, '文件管理', '', 2, 6, 2, 'file', 'ep:files', NULL, '', 0, b'1', b'1', b'1', '1', '2022-03-16 23:47:40', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (194, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (194, '作者动态', '', 1, 0, 0, 'https://www.iocoder.cn', 'ep:avatar', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-04-23 01:03:15', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (195, '数据源配置', '', 2, 1, 2, 'data-source-config', 'ep:data-analysis', 'infra/dataSourceConfig/index', 'InfraDataSourceConfig', 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (196, '数据源配置查询', 'infra:data-source-config:query', 3, 1, 195, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (197, '数据源配置创建', 'infra:data-source-config:create', 3, 2, 195, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-04-27 14:37:32', '', '2026-08-14 01:49:42', b'0');
@@ -2446,7 +2523,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (204, '客户端创建', 'system:oauth2-client:create', 3, 2, 202, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-05-10 16:26:33', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (205, '客户端更新', 'system:oauth2-client:update', 3, 3, 202, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-05-10 16:26:33', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (206, '客户端删除', 'system:oauth2-client:delete', 3, 4, 202, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-05-10 16:26:33', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (207, '报表管理', '', 2, 40, 0, '/report', 'ep:pie-chart', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (207, '报表管理', '', 2, 40, 0, '/report', 'ep:pie-chart', NULL, NULL, 0, b'1', b'1', b'1', '1', '2022-07-10 20:22:15', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (208, '报表设计器', '', 2, 1, 207, 'jimu-report', 'ep:trend-charts', 'report/jmreport/index', 'JimuReport', 0, b'1', b'1', b'1', '1', '2022-07-10 20:26:36', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (209, '商品中心', '', 1, 60, 449, 'product', 'fa:product-hunt', NULL, NULL, 0, b'1', b'1', b'1', '', '2022-07-29 15:53:53', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (210, '商品分类', '', 2, 2, 209, 'category', 'ep:cellphone', 'mall/product/category/index', 'ProductCategory', 0, b'1', b'1', b'1', '', '2022-07-29 15:53:53', '1', '2026-08-14 01:49:42', b'0');
@@ -2586,8 +2663,8 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (344, '查询项目', 'report:go-view-project:query', 3, 0, 341, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:25:53', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (345, '使用 SQL 查询数据', 'report:go-view-data:get-by-sql', 3, 3, 341, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:26:15', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (346, '使用 HTTP 查询数据', 'report:go-view-data:get-by-http', 3, 4, 341, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:26:35', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (347, 'Boot 开发文档', '', 1, 1, 0, 'https://doc.iocoder.cn/', 'ep:document', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:46:28', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (348, 'Cloud 开发文档', '', 1, 2, 0, 'https://cloud.iocoder.cn', 'ep:document-copy', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:47:07', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (347, 'Boot 开发文档', '', 1, 1, 0, 'https://doc.iocoder.cn/', 'ep:document', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:46:28', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (348, 'Cloud 开发文档', '', 1, 2, 0, 'https://cloud.iocoder.cn', 'ep:document-copy', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:47:07', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (349, '接入示例', '', 1, 99, 114, 'demo', 'fa-solid:dragon', 'pay/demo/index', NULL, 0, b'1', b'1', b'1', '', '2023-02-11 14:21:42', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (350, '商品导出', 'product:spu:export', 3, 5, 220, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 14:22:58', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (351, '配送管理', '', 1, 3, 266, 'delivery', 'ep:shopping-cart', '', '', 0, b'1', b'1', b'1', '1', '2023-05-18 09:18:02', '1', '2026-08-14 01:49:42', b'0');
@@ -2612,7 +2689,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (370, '自提门店删除', 'trade:delivery:pick-up-store:delete', 3, 4, 366, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (371, '自提门店导出', 'trade:delivery:pick-up-store:export', 3, 5, 366, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-05-25 10:53:29', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (372, '秒杀活动', '', 2, 3, 235, 'seckill', 'ep:place', '', '', 0, b'1', b'1', b'1', '1', '2023-06-24 17:39:13', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (373, '会员中心', '', 1, 50, 0, '/member', 'ep:bicycle', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-06-10 00:42:03', '1', '2026-08-22 20:11:18', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (373, '会员中心', '', 1, 50, 0, '/member', 'ep:bicycle', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-06-10 00:42:03', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (374, '会员配置', '', 2, 0, 373, 'config', 'fa:archive', 'member/config/index', 'MemberConfig', 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (375, '会员配置查询', 'member:config:query', 3, 1, 374, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (376, '会员配置保存', 'member:config:save', 3, 2, 374, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-06-10 02:07:44', '1', '2026-08-14 01:49:42', b'0');
@@ -2688,7 +2765,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (446, '交易统计', '', 2, 4, 445, 'trade', 'fa-solid:credit-card', 'mall/statistics/trade/index', 'TradeStatistics', 0, b'1', b'1', b'1', '', '2023-09-30 03:22:40', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (447, '交易统计查询', 'statistics:trade:query', 3, 1, 446, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-30 03:22:40', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (448, '交易统计导出', 'statistics:trade:export', 3, 2, 446, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-09-30 03:22:40', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (449, 'MALL 商城系统', '', 1, 59, 0, '/mall', 'ep:shop', '', '', 0, b'1', b'1', b'1', '1', '2023-09-30 11:52:02', '1', '2026-08-22 20:19:34', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (449, 'MALL 商城系统', '', 1, 59, 0, '/mall', 'ep:shop', '', '', 0, b'1', b'1', b'1', '1', '2023-09-30 11:52:02', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (450, '用户积分修改', 'member:user:update-point', 3, 6, 404, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-01 14:39:43', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (451, '用户余额修改', 'pay:wallet:update-balance', 3, 7, 404, '', '', '', '', 0, b'1', b'1', b'1', '', '2023-10-01 14:39:43', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (452, '优惠劵', '', 1, 2, 235, 'coupon', 'fa-solid:disease', '', '', 0, b'1', b'1', b'1', '1', '2023-10-03 12:39:15', '1', '2026-08-14 01:49:42', b'0');
@@ -2719,7 +2796,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (477, '客户更新', 'crm:customer:update', 3, 3, 474, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (478, '客户删除', 'crm:customer:delete', 3, 4, 474, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (479, '客户导出', 'crm:customer:export', 3, 5, 474, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 09:04:21', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (480, 'CRM 系统', '', 1, 200, 0, '/crm', 'simple-icons:civicrm', '', '', 0, b'1', b'1', b'1', '1', '2023-10-29 17:08:30', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (480, 'CRM 系统', '', 1, 200, 0, '/crm', 'simple-icons:civicrm', '', '', 0, b'1', b'1', b'1', '1', '2023-10-29 17:08:30', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (481, '合同管理', '', 2, 50, 480, 'contract', 'ep:notebook', 'crm/contract/index', 'CrmContract', 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (482, '合同查询', 'crm:contract:query', 3, 1, 481, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (483, '合同创建', 'crm:contract:create', 3, 2, 481, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2023-10-29 10:50:41', '', '2026-08-14 01:49:42', b'0');
@@ -2836,7 +2913,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (594, '数据统计', '', 1, 200, 480, 'statistics', 'ep:data-line', '', '', 0, b'1', b'1', b'1', '1', '2024-01-26 22:50:35', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (595, '排行榜', 'crm:statistics-rank:query', 2, 1, 594, 'ranking', 'fa:area-chart', 'crm/statistics/rank/index', 'CrmStatisticsRank', 0, b'1', b'1', b'1', '1', '2024-01-26 22:52:09', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (596, '客户导入', 'crm:customer:import', 3, 6, 474, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-02-01 13:09:00', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (597, 'ERP 系统', '', 1, 300, 0, '/erp', 'simple-icons:erpnext', '', '', 0, b'1', b'1', b'1', '1', '2024-02-04 15:37:25', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (597, 'ERP 系统', '', 1, 300, 0, '/erp', 'simple-icons:erpnext', '', '', 0, b'1', b'1', b'1', '1', '2024-02-04 15:37:25', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (598, '产品管理', '', 1, 40, 597, 'product', 'fa:product-hunt', '', '', 0, b'1', b'1', b'1', '1', '2024-02-04 15:38:43', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (599, '产品信息', '', 2, 0, 598, 'product', 'fa-solid:apple-alt', 'erp/product/product/index', 'ErpProduct', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (600, '产品查询', 'erp:product:query', 3, 1, 599, '', '', '', '', 0, b'1', b'1', b'1', '', '2024-02-04 07:52:15', '1', '2026-08-14 01:49:42', b'0');
@@ -3030,7 +3107,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (788, '删除项目', 'report:go-view-project:delete', 3, 2, 341, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:01:37', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (789, '会员等级记录查询', 'member:level-record:query', 3, 10, 412, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:02:32', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (790, '会员经验记录查询', 'member:experience-record:query', 3, 11, 412, '', '', '', '', 0, b'1', b'1', b'1', '1', '2024-04-24 20:02:51', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (791, 'AI 大模型', '', 1, 400, 0, '/ai', 'tabler:ai', '', '', 0, b'1', b'1', b'1', '1', '2024-05-07 15:07:56', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (791, 'AI 大模型', '', 1, 400, 0, '/ai', 'tabler:ai', '', '', 0, b'1', b'1', b'1', '1', '2024-05-07 15:07:56', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (792, 'AI 对话', '', 2, 1, 791, 'chat', 'ep:message', 'ai/chat/index/index.vue', 'AiChat', 0, b'1', b'1', b'1', '1', '2024-05-07 15:09:14', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (793, '控制台', '', 1, 100, 791, 'console', 'ep:setting', '', '', 0, b'1', b'1', b'1', '1', '2024-05-09 22:39:09', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (794, 'API 密钥', '', 2, 0, 793, 'api-key', 'ep:key', 'ai/model/apiKey/index.vue', 'AiApiKey', 0, b'1', b'1', b'1', '', '2024-05-09 14:52:56', '1', '2026-08-14 01:49:42', b'0');
@@ -3097,7 +3174,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (857, '工具创建', 'ai:tool:create', 3, 2, 855, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (858, '工具更新', 'ai:tool:update', 3, 3, 855, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (859, '工具删除', 'ai:tool:delete', 3, 4, 855, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2025-03-14 11:19:29', '', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (860, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:55:28', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (860, 'IoT 物联网', '', 1, 500, 0, '/iot', 'fa-solid:hdd', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:55:28', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (861, '设备接入', '', 1, 2, 860, 'device', 'ep:platform', '', '', 0, b'1', b'1', b'1', '1', '2024-08-10 09:57:56', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (862, '产品管理', '', 2, 1, 861, 'product', 'fa-solid:tools', 'iot/product/product/index', 'IoTProduct', 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (863, '产品查询', 'iot:product:query', 3, 1, 862, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2024-08-10 02:38:02', '', '2026-08-14 01:49:42', b'0');
@@ -3183,7 +3260,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (954, '删除模版消息', 'mp:message-template:delete', 3, 2, 952, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-11-26 17:00:31', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (955, '同步公众号模板', 'mp:message-template:sync', 3, 3, 952, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-11-26 17:00:55', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (956, '给粉丝发送模版消息', 'mp:message-template:send', 3, 4, 952, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-11-26 17:01:11', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (959, 'MES 系统', '', 1, 320, 0, '/mes', 'ep:cpu', '', '', 0, b'1', b'1', b'1', '1', '2026-02-15 00:39:57', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (959, 'MES 系统', '', 1, 320, 0, '/mes', 'ep:cpu', '', '', 0, b'1', b'1', b'1', '1', '2026-02-15 00:39:57', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (960, '基础数据', '', 1, 10, 959, 'md', 'ep:data-analysis', '', '', 0, b'1', b'1', b'1', '1', '2026-02-15 00:40:13', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (961, '物料产品分类', '', 2, 2, 960, 'item-type', 'ep:files', 'mes/md/item/type/index', 'MesMdItemType', 0, b'1', b'1', b'1', '1', '2026-02-15 00:40:13', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (962, '分类查询', 'mes:md-item-type:query', 3, 1, 961, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-02-15 00:40:13', '1', '2026-08-14 01:49:42', b'0');
@@ -3568,7 +3645,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1345, '工作记录导出', 'mes:pro-workrecord:export', 3, 2, 1343, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-04-05 14:08:44', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1346, '上工下工', 'mes:pro-workrecord:clock', 3, 3, 1343, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-04-05 14:08:44', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1347, 'MES 首页', 'mes:home:query', 2, 0, 959, 'mes/home/index', 'ep:home-filled', 'mes/home/index', 'MesHome', 0, b'1', b'1', b'1', '1', '2026-04-05 23:24:03', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1348, 'WMS 系统', '', 1, 310, 0, '/wms', 'ep:box', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1348, 'WMS 系统', '', 1, 310, 0, '/wms', 'ep:box', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1349, '基础数据', '', 1, 6, 1348, 'md', 'ep:files', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1350, '仓库管理', '', 2, 1, 1349, 'warehouse', 'ep:office-building', 'wms/md/warehouse/index', 'WmsWarehouse', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1351, '仓库查询', 'wms:warehouse:query', 3, 1, 1350, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-08-14 01:49:42', b'0');
@@ -3637,7 +3714,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1415, '盘库单作废', 'wms:check-order:cancel', 3, 6, 1409, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1416, '盘库单导出', 'wms:check-order:export', 3, 7, 1409, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1417, 'WMS 首页', 'wms:home:query', 2, 0, 1348, 'home', 'ep:home-filled', 'wms/home/index', 'WmsHome', 0, b'1', b'1', b'1', '1', '2026-05-14 09:34:27', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1418, 'IM 即时通讯', '', 1, 501, 0, '/im', 'ep:chat-dot-round', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2026-04-30 09:11:20', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1418, 'IM 即时通讯', '', 1, 501, 0, '/im', 'ep:chat-dot-round', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2026-04-30 09:11:20', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1419, '数据统计', 'im:manager:statistics:query', 2, 10, 1418, 'statistics', 'ep:trend-charts', 'im/manager/statistics/index', 'ImStatistics', 0, b'1', b'1', b'1', 'admin', '2026-04-30 09:11:20', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1420, '好友申请', 'im:manager:friend-request:query', 2, 20, 1456, 'friend-request', 'ep:document', 'im/manager/friend/request/index', 'ImFriendRequest', 0, b'1', b'1', b'1', '1', '2026-05-05 11:15:48', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1421, '好友列表', 'im:manager:friend:list', 2, 10, 1456, 'friend', 'ep:user', 'im/manager/friend/index', 'ImFriend', 0, b'1', b'1', b'1', 'admin', '2026-04-30 09:11:20', '1', '2026-08-14 01:49:42', b'0');
@@ -3695,7 +3772,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1473, '立即推送', 'im:manager:channel-message:send', 3, 2, 1471, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1474, '消息删除', 'im:manager:channel-message:delete', 3, 3, 1471, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1475, '解散群', 'im:manager:group:dissolve', 3, 21, 1425, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-24 12:02:38', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1476, 'HRM 人力资源', '', 1, 350, 0, '/hrm', 'ep:office-building', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2026-07-18 09:22:43', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1476, 'HRM 人力资源', '', 1, 350, 0, '/hrm', 'ep:office-building', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2026-07-18 09:22:43', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1477, '员工管理', '', 1, 20, 1476, 'employee', 'ep:user-filled', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2026-07-18 09:22:43', 'admin', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1478, '员工档案', 'hrm:employee:query', 2, 1, 1477, 'list', 'ep:user', 'hrm/employee/index', 'HrmEmployee', 0, b'1', b'1', b'1', 'admin', '2026-07-18 09:22:43', 'admin', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1479, '员工查询', 'hrm:employee:query', 3, 1, 1478, '', '', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2026-07-18 09:22:43', 'admin', '2026-08-14 01:49:42', b'0');
@@ -4042,7 +4119,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1861, '绩效类型更新', 'cms:performance-type:update', 3, 3, 1858, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-07-08 17:33:49', 'admin', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1862, '绩效类型删除', 'cms:performance-type:delete', 3, 4, 1858, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-07-08 17:33:49', 'admin', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1863, '绩效类型导出', 'cms:performance-type:export', 3, 5, 1858, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-07-08 17:33:49', 'admin', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1894, 'FMS 财务管理', '', 1, 360, 0, '/fms', 'ep:money', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-02 01:30:15', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1894, 'FMS 财务管理', '', 1, 360, 0, '/fms', 'ep:money', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-02 01:30:15', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1895, '账簿管理', '', 1, 20, 1894, 'ledger', 'ep:notebook', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-02 01:30:15', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1896, '账套管理', '', 2, 0, 1951, 'account-set', 'ep:office-building', 'fms/config/account-set/index', 'FmsAccountSet', 0, b'1', b'1', b'1', '1', '2026-08-02 01:30:15', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1897, '账套查询', 'fms:config:account-set:query', 3, 1, 1896, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-08-02 01:30:15', '1', '2026-08-14 01:49:42', b'0');
@@ -4175,7 +4252,7 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2024, '核算项目余额表导出', 'fms:ledger:subject-balance:export', 3, 2, 1930, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-08-02 12:12:03', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2025, '核算项目余额表打印', 'fms:ledger:subject-balance:print', 3, 3, 1930, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-08-11 19:23:23', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2026, '初始余额导出', 'fms:config:initial-balance:export', 3, 4, 1932, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-08-02 12:53:37', '1', '2026-08-14 01:49:42', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8000, 'PMS 项目管理', '', 1, 370, 0, '/pms', 'ep:management', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-18 21:42:32', '1', '2026-08-22 20:11:26', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8000, 'PMS 项目管理', '', 1, 370, 0, '/pms', 'ep:management', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-18 21:42:32', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8010, '项目分组', '', 2, 90, 8080, 'project/list/group', 'ep:folder', 'pms/pm/project/list/components/group/index', 'PmsProjectGroup', 0, b'0', b'0', b'0', '1', '2026-08-18 21:42:32', '1', '2026-08-27 10:01:06', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8011, '项目分组查询', 'pms:pm:project-group:query', 3, 1, 8010, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-18 21:42:32', '1', '2026-08-20 09:05:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8012, '项目分组创建', 'pms:pm:project-group:create', 3, 2, 8010, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-18 21:42:32', '1', '2026-08-20 09:05:58', b'0');
@@ -4199,21 +4276,31 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8035, '工作项导入', 'pms:pm:work-item:import', 3, 15, 8020, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-19 04:37:59', '1', '2026-08-20 09:05:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8036, '工作项导出', 'pms:pm:work-item:export', 3, 16, 8020, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-19 04:37:59', '1', '2026-08-20 09:05:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8040, '工作台', 'pms:pm:work-item:query', 2, 10, 8080, 'workbench', 'ep:list', 'pms/pm/workbench/index', 'PmsWorkbench', 0, b'1', b'1', b'0', '1', '2026-08-19 04:14:10', '1', '2026-08-20 09:05:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8050, '知识库', '', 2, 10, 8090, 'library', 'ep:notebook', 'pms/kb/library/index', 'PmsKnowledgeLibrary', 0, b'1', b'1', b'0', '1', '2026-08-19 00:28:42', '1', '2026-08-25 14:46:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8051, '知识库查询', 'pms:kb:library:query', 3, 1, 8050, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-19 00:28:42', '1', '2026-08-23 16:21:39', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8050, '知识库', '', 2, 10, 8090, 'library', 'ep:notebook', 'pms/kb/library/index', 'PmsKnowledgeLibrary', 0, b'1', b'1', b'0', '1', '2026-08-19 00:28:42', '1', '2026-09-02 13:02:13', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8051, '知识库查询', 'pms:kb:library:query', 3, 1, 8050, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-19 00:28:42', '1', '2026-09-02 13:03:24', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8052, '知识库创建', 'pms:kb:library:create', 3, 2, 8050, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-19 00:28:42', '1', '2026-08-23 16:21:39', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8053, '知识库修改', 'pms:kb:library:update', 3, 3, 8050, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-19 00:28:42', '1', '2026-08-23 16:21:39', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8054, '知识库删除', 'pms:kb:library:delete', 3, 4, 8050, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-19 00:28:42', '1', '2026-08-23 16:21:39', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8060, '我的关注', 'pms:kb:knowledge-library:query', 2, 40, 8090, 'favorite', 'ep:star', 'pms/kb/favorite/index', 'PmsKnowledgeFavorite', 0, b'1', b'1', b'0', '1', '2026-08-19 03:05:49', '1', '2026-08-23 16:12:56', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8060, '我的关注', 'pms:kb:knowledge-library:query', 2, 40, 8090, 'favorite', 'ep:star', 'pms/kb/favorite/index', 'PmsKnowledgeFavorite', 0, b'1', b'1', b'0', '1', '2026-08-19 03:05:49', '1', '2026-09-03 18:02:33', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8070, '最近使用', 'pms:kb:knowledge-library:query', 2, 50, 8090, 'recent', 'ep:clock', 'pms/kb/recent/index', 'PmsKnowledgeRecent', 0, b'1', b'1', b'0', '1', '2026-08-19 03:05:49', '1', '2026-08-23 16:12:56', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8080, '项目中心', '', 1, 10, 8000, 'pm', 'ep:grid', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-19 11:42:30', '1', '2026-08-20 09:05:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8080, '项目中心', '', 1, 10, 8000, 'pm', 'ep:grid', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-19 11:42:30', '1', '2026-09-02 13:02:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8081, '归档项目', 'pms:pm:project:query', 2, 30, 8080, 'project/archive', 'ep:box', 'pms/pm/project/archive/index', 'PmsProjectArchive', 0, b'1', b'0', b'0', '1', '2026-08-19 11:42:30', '1', '2026-08-27 10:01:06', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8082, '项目回收站', 'pms:pm:project:query', 2, 40, 8080, 'project/recycle', 'ep:delete', 'pms/pm/project/recycle/index', 'PmsProjectRecycle', 0, b'1', b'0', b'0', '1', '2026-08-19 11:42:30', '1', '2026-08-27 10:01:06', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8090, '知识中心', '', 1, 20, 8000, 'kb', 'ep:notebook', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-19 11:42:30', '1', '2026-08-20 09:05:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8090, '知识中心', '', 1, 20, 8000, 'kb', 'ep:notebook', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-19 11:42:30', '1', '2026-09-02 13:02:27', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8091, '回收站', 'pms:kb:knowledge-library:delete', 2, 60, 8090, 'recycle', 'ep:delete', 'pms/kb/recycle/index', 'PmsKnowledgeRecycle', 0, b'1', b'0', b'0', '1', '2026-08-19 11:42:30', '1', '2026-08-23 16:23:05', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8092, '文档搜索', 'pms:kb:library:query', 2, 20, 8090, 'search', 'ep:search', 'pms/kb/search/index', 'PmsKnowledgeSearch', 0, b'1', b'1', b'0', '1', '2026-08-23 16:12:56', '1', '2026-08-23 16:23:05', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8093, '文档标签', 'pms:kb:library:query', 2, 30, 8090, 'label', 'ep:collection-tag', 'pms/kb/label/index', 'PmsKnowledgeDocumentLabel', 0, b'1', b'1', b'0', '1', '2026-08-23 16:12:56', '1', '2026-08-23 16:12:56', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8200, 'OA 办公协同', '', 1, 345, 0, '/oa', 'ep:office-building', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-24 00:23:36', '1', '2026-08-25 09:33:56', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8094, '知识库模板', '', 2, 70, 8090, 'library-template', 'ep:files', 'pms/kb/library-template/index', 'PmsKnowledgeLibraryTemplate', 0, b'1', b'0', b'0', '1', '2026-09-01 13:53:37', '1', '2026-09-02 13:03:17', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8095, '知识库模板查询', 'pms:kb:library-template:query', 3, 1, 8094, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-09-01 13:53:37', '1', '2026-09-01 13:53:37', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8096, '知识库模板创建', 'pms:kb:library-template:create', 3, 2, 8094, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-09-01 13:53:37', '1', '2026-09-01 13:53:37', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8097, '知识库模板修改', 'pms:kb:library-template:update', 3, 3, 8094, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-09-01 13:53:37', '1', '2026-09-01 13:53:37', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8098, '知识库模板删除', 'pms:kb:library-template:delete', 3, 4, 8094, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-09-01 13:53:37', '1', '2026-09-01 13:53:37', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8100, '项目模板', '', 2, 50, 8080, 'project/template', 'ep:files', 'pms/pm/project/template/index', 'PmsProjectTemplate', 0, b'1', b'0', b'0', '1', '2026-08-31 21:38:15', '1', '2026-08-31 21:38:15', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8101, '项目模板查询', 'pms:pm:project-template:query', 3, 1, 8100, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-31 21:38:15', '1', '2026-08-31 21:38:15', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8102, '项目模板创建', 'pms:pm:project-template:create', 3, 2, 8100, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-31 21:38:15', '1', '2026-08-31 21:38:15', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8103, '项目模板修改', 'pms:pm:project-template:update', 3, 3, 8100, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-31 21:38:15', '1', '2026-08-31 21:38:15', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8104, '项目模板删除', 'pms:pm:project-template:delete', 3, 4, 8100, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-31 21:38:15', '1', '2026-08-31 21:38:15', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8200, 'OA 办公协同', '', 1, 345, 0, '/oa', 'ep:office-building', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-08-24 00:23:36', '1', '2026-09-02 18:42:01', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8210, '日程管理', 'oa:schedule:query', 2, 10, 8200, 'schedule', 'ep:calendar', 'oa/schedule/index', 'OaSchedule', 0, b'1', b'1', b'0', '1', '2026-08-24 00:23:36', '1', '2026-08-24 00:23:36', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8211, '日程新增', 'oa:schedule:create', 3, 1, 8210, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-24 00:23:36', '1', '2026-08-24 00:23:36', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8212, '日程修改', 'oa:schedule:update', 3, 2, 8210, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-24 00:23:36', '1', '2026-08-24 00:23:36', b'0');
@@ -4240,11 +4327,8 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8262, '讨论修改', 'oa:discussion:update', 3, 2, 8260, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-24 00:23:36', '1', '2026-08-24 00:23:36', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8263, '讨论删除', 'oa:discussion:delete', 3, 3, 8260, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-24 00:23:36', '1', '2026-08-24 00:23:36', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (8264, '讨论互动', 'oa:discussion:interact', 3, 4, 8260, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-08-24 00:23:36', '1', '2026-08-24 00:23:36', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12016, '栏目整理树', 'cms:channel:tidy', 3, 6, 12010, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12081, '内容块项排序', 'cms:block-item:update-order', 3, 6, 12070, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12082, '内容块排序', 'cms:block:update-order', 3, 6, 12490, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12083, '内容块排序', 'cms:block:update-order', 3, 5, 1831, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-17 15:25:49', 'admin', '2026-08-17 15:25:49', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12090, '模板文件', '', 2, 1, 12430, 'template', 'ep:document', 'cms/file/template/index', 'CmsTemplateFile', 0, b'1', b'1', b'1', 'admin', '2026-08-22 16:29:52', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12090, '模板文件', '', 2, 1, 1803, 'template', 'ep:document', 'cms/file/template/index', 'CmsTemplateFile', 0, b'1', b'1', b'1', 'admin', '2026-08-22 16:29:52', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12091, '模板文件查询', 'cms:template-file:query', 3, 1, 12090, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 16:29:52', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12092, '模板文件创建', 'cms:template-file:create', 3, 2, 12090, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 16:29:52', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12093, '模板文件更新', 'cms:template-file:update', 3, 3, 12090, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 16:29:52', 'admin', '2026-08-27 22:01:14', b'0');
@@ -4258,71 +4342,23 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12131, '表单提交审核', 'cms:form-submission:audit', 3, 1, 12121, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-23 19:05:44', 'admin', '2026-08-27 21:28:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12132, '表单提交', 'cms:form-submission:submit', 3, 10, 1708, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-26 20:03:34', 'admin', '2026-08-27 21:28:58', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12155, '问卷反馈更新', 'cms:survey-feedback:update', 3, 15, 1719, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-19 00:44:22', 'admin', '2026-08-22 20:12:52', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12233, '采集预览', 'cms:collection:preview', 3, 9, 12220, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12234, '采集执行', 'cms:collection:execute', 3, 10, 12220, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12235, '采集清空记录', 'cms:collection:clear-records', 3, 11, 12220, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-19 00:44:19', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12400, '工作台', '', 1, 0, 12000, 'workspace', 'ep:monitor', NULL, 'CmsWorkspaceGroup', 0, b'0', b'1', b'0', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12410, '内容管理', '', 1, 1, 12000, 'content', 'ep:document', NULL, 'CmsContentGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12420, '互动', '', 1, 2, 12000, 'interaction', 'ep:chat-line-square', NULL, 'CmsInteractionGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12430, '文件管理', '', 1, 3, 12000, 'file', 'ep:files', NULL, 'CmsFileGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12440, '配置', '', 1, 4, 12000, 'config', 'ep:setting', NULL, 'CmsConfigGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12441, '全局设置', '', 2, 1, 12440, 'global', 'ep:setting', 'cms/config/global/index', 'CmsGlobalConfig', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12442, '全局设置查询', 'cms:config:query', 3, 1, 12441, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12443, '全局设置更新', 'cms:config:update', 3, 2, 12441, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12450, '统计', '', 1, 5, 12000, 'stat', 'ep:data-analysis', NULL, 'CmsStatGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12461, '文章统计', '', 1, 3, 12450, 'article', 'ep:histogram', NULL, 'CmsArticleStatGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12462, '文章统计查询', 'cms:article-stat:query', 3, 1, 12461, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12463, '文章统计导出', 'cms:article-stat:export', 3, 2, 12461, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12464, '绩效统计', '', 1, 4, 12450, 'performance', 'ep:trophy', NULL, 'CmsPerformanceStatGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12465, '绩效统计查询', 'cms:performance-stat:query', 3, 1, 12464, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12466, '绩效统计导出', 'cms:performance-stat:export', 3, 2, 12464, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12470, 'HTML 文件', '', 2, 3, 12430, 'html', 'ep:document', 'cms/file/html/index', 'CmsHtmlFile', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12471, 'HTML 文件查询', 'cms:html-file:query', 3, 1, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12472, 'HTML 文件下载', 'cms:html-file:download', 3, 2, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12473, 'HTML 文件删除', 'cms:html-file:delete', 3, 3, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12474, 'HTML 文件更新', 'cms:html-file:update', 3, 4, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12475, 'HTML 文件创建', 'cms:html-file:create', 3, 5, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12476, 'HTML 文件上传', 'cms:html-file:upload', 3, 6, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12477, 'HTML 文件 ZIP', 'cms:html-file:zip', 3, 7, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12478, 'HTML 文件复制', 'cms:html-file:copy', 3, 8, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12479, 'HTML 文件移动', 'cms:html-file:move', 3, 9, 12470, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12480, '上传文件', '', 2, 4, 12430, 'upload-file', 'ep:upload', 'cms/file/upload/index', 'CmsUploadFile', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12481, '上传文件查询', 'cms:upload-file:query', 3, 1, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12482, '上传文件创建', 'cms:upload-file:create', 3, 2, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12483, '上传文件上传', 'cms:upload-file:upload', 3, 3, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12484, '上传文件下载', 'cms:upload-file:download', 3, 4, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12485, '上传文件删除', 'cms:upload-file:delete', 3, 5, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12486, '上传文件重命名', 'cms:upload-file:update', 3, 6, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12487, '上传文件复制', 'cms:upload-file:copy', 3, 7, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12488, '上传文件移动', 'cms:upload-file:move', 3, 8, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12489, '上传文件 ZIP', 'cms:upload-file:zip', 3, 9, 12480, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12490, '区块设置', '', 2, 4, 12440, 'block', 'ep:grid', 'cms/content/block/index', 'CmsBlockConfig', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12500, '文章提交', 'cms:article:submit', 3, 6, 12040, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12501, '文章归档', 'cms:article:archive', 3, 7, 12040, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12502, '文章下线', 'cms:article:offline', 3, 8, 12040, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12503, '文章彻底删除', 'cms:article:purge', 3, 9, 12040, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12504, '文章置顶', 'cms:article:sticky', 3, 10, 12040, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12505, '文章排序', 'cms:article:update-order', 3, 11, 12040, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12506, '文章审核', '', 2, 2, 12410, 'article-audit', 'ep:stamp', 'cms/content/article/audit/index', 'CmsArticleAudit', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12507, '文章审核查询', 'cms:article:audit', 3, 1, 12506, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12526, '留言类型', '', 2, 8, 12440, 'message-board-type', 'ep:chat-dot-square', 'cms/interaction/message-board/type/index', 'CmsMessageBoardType', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12540, '数据导入', '', 2, 11, 12410, 'import-data', 'ep:upload-filled', 'cms/content/import-data/index', 'CmsImportData', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12235, '采集清空记录', 'cms:collection:clear-records', 3, 11, 1766, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-19 00:44:19', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12475, 'HTML 文件创建', 'cms:html-file:create', 3, 5, 1816, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12476, 'HTML 文件上传', 'cms:html-file:upload', 3, 6, 1816, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12477, 'HTML 文件 ZIP', 'cms:html-file:zip', 3, 7, 1816, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12478, 'HTML 文件复制', 'cms:html-file:copy', 3, 8, 1816, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12479, 'HTML 文件移动', 'cms:html-file:move', 3, 9, 1816, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12550, '评论管理', '', 2, 2, 1802, 'comment', 'ep:comment', 'cms/interaction/comment/index', 'CmsComment', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-28 17:48:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12551, '评论查询', 'cms:comment:query', 3, 1, 12550, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12552, '评论更新', 'cms:comment:update', 3, 2, 12550, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12553, '评论状态更新', 'cms:comment:update-status', 3, 3, 12550, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12554, '评论删除', 'cms:comment:delete', 3, 4, 12550, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12560, '绩效类型', '', 2, 7, 12440, 'performance-type', 'ep:medal', 'cms/stat/performance/type/index', 'CmsPerformanceType', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12561, '绩效类型查询', 'cms:performance-type:query', 3, 1, 12560, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12562, '绩效类型创建', 'cms:performance-type:create', 3, 2, 12560, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12563, '绩效类型更新', 'cms:performance-type:update', 3, 3, 12560, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12564, '绩效类型删除', 'cms:performance-type:delete', 3, 4, 12560, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12570, '表单类型', '', 2, 9, 1804, 'form-type', 'ep:tickets', 'cms/interaction/form/type/index', 'CmsFormType', 0, b'1', b'1', b'1', 'admin', '2026-08-23 19:05:44', 'admin', '2026-08-25 09:50:35', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12571, '表单类型查询', 'cms:form-type:query', 3, 1, 12570, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-23 19:05:44', 'admin', '2026-08-23 19:06:11', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12572, '表单类型创建', 'cms:form-type:create', 3, 2, 12570, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-23 19:05:44', 'admin', '2026-08-23 19:06:11', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12573, '表单类型更新', 'cms:form-type:update', 3, 3, 12570, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-23 19:05:44', 'admin', '2026-08-23 19:06:11', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12574, '表单类型删除', 'cms:form-type:delete', 3, 4, 12570, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-23 19:05:44', 'admin', '2026-08-23 19:06:11', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12580, '附件管理', '', 2, 8, 12410, 'attachment', 'ep:paperclip', 'cms/content/attachment/index', 'CmsAttachment', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12580, '附件管理', '', 2, 8, 1801, 'attachment', 'ep:paperclip', 'cms/content/attachment/index', 'CmsAttachment', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12581, '附件查询', 'cms:attachment:query', 3, 1, 12580, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12582, '附件删除', 'cms:attachment:delete', 3, 2, 12580, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12600, '友情链接', '', 2, 9, 1801, 'friend-link', 'ep:link', 'cms/content/friend-link/index', 'CmsFriendLink', 0, b'1', b'1', b'1', 'admin', '2026-08-24 01:35:41', 'admin', '2026-08-25 09:56:19', b'0');
@@ -4335,20 +4371,20 @@ INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_i
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12612, '友情链接类型创建', 'cms:friend-link-type:create', 3, 2, 12610, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-24 01:35:41', 'admin', '2026-08-24 01:35:41', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12613, '友情链接类型更新', 'cms:friend-link-type:update', 3, 3, 12610, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-24 01:35:41', 'admin', '2026-08-24 01:35:41', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12614, '友情链接类型删除', 'cms:friend-link-type:delete', 3, 4, 12610, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-24 01:35:41', 'admin', '2026-08-24 01:35:41', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12690, '全局文件管理', 'cms:file:global', 3, 1, 12430, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12700, '访问趋势', '', 2, 1, 12200, 'visit-trend', '', 'cms/stat/visit/trend/index', 'CmsVisitTrend', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12701, '受访页面', '', 2, 2, 12200, 'visited-page', '', 'cms/stat/visit/visited-page/index', 'CmsVisitVisitedPage', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12702, '入口页面', '', 2, 3, 12200, 'entry-page', '', 'cms/stat/visit/entry-page/index', 'CmsVisitEntryPage', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12703, '访问来源', '', 2, 4, 12200, 'source', '', 'cms/stat/visit/source/index', 'CmsVisitSource', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12710, '访客分析', '', 1, 2, 12450, 'visitor', 'ep:user', NULL, 'CmsVisitorAnalysisGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12690, '全局文件管理', 'cms:file:global', 3, 1, 1803, '', '', '', '', 0, b'1', b'1', b'1', 'admin', '2026-08-22 19:03:22', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12700, '访问趋势', '', 2, 1, 1759, 'visit-trend', '', 'cms/stat/visit/trend/index', 'CmsVisitTrend', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12701, '受访页面', '', 2, 2, 1759, 'visited-page', '', 'cms/stat/visit/visited-page/index', 'CmsVisitVisitedPage', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12702, '入口页面', '', 2, 3, 1759, 'entry-page', '', 'cms/stat/visit/entry-page/index', 'CmsVisitEntryPage', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12703, '访问来源', '', 2, 4, 1759, 'source', '', 'cms/stat/visit/source/index', 'CmsVisitSource', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12710, '访客分析', '', 1, 2, 1808, 'visitor', 'ep:user', NULL, 'CmsVisitorAnalysisGroup', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12711, '新老访客', '', 2, 1, 12710, 'new-old', '', 'cms/stat/visit/visitor/index', 'CmsVisitVisitor', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12712, '地域分布', '', 2, 2, 12710, 'region', '', 'cms/stat/visit/region/index', 'CmsVisitRegion', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12713, '访客环境', '', 2, 3, 12710, 'environment', '', 'cms/stat/visit/environment/index', 'CmsVisitEnvironment', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12720, '按用户统计', '', 2, 1, 12461, 'user', '', 'cms/stat/article/user/index', 'CmsArticleStatUser', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12721, '按组织统计', '', 2, 2, 12461, 'org', '', 'cms/stat/article/org/index', 'CmsArticleStatOrg', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12722, '按栏目统计', '', 2, 3, 12461, 'channel', '', 'cms/stat/article/channel/index', 'CmsArticleStatChannel', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12730, '用户绩效', '', 2, 1, 12464, 'user', '', 'cms/stat/performance/user/index', 'CmsPerformanceStatUser', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12731, '组织绩效', '', 2, 2, 12464, 'org', '', 'cms/stat/performance/org/index', 'CmsPerformanceStatOrg', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 22:01:14', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12720, '按用户统计', '', 2, 1, 1810, 'user', '', 'cms/stat/article/user/index', 'CmsArticleStatUser', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12721, '按组织统计', '', 2, 2, 1810, 'org', '', 'cms/stat/article/org/index', 'CmsArticleStatOrg', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12722, '按栏目统计', '', 2, 3, 1810, 'channel', '', 'cms/stat/article/channel/index', 'CmsArticleStatChannel', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12730, '用户绩效', '', 2, 1, 1813, 'user', '', 'cms/stat/performance/user/index', 'CmsPerformanceStatUser', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (12731, '组织绩效', '', 2, 2, 1813, 'org', '', 'cms/stat/performance/org/index', 'CmsPerformanceStatOrg', 0, b'1', b'1', b'1', 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-31 21:39:16', b'0');
COMMIT;
-- ----------------------------
@@ -4544,7 +4580,7 @@ CREATE TABLE `system_oauth2_access_token` (
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_access_token`(`access_token` ASC) USING BTREE,
INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 121205 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
) ENGINE = InnoDB AUTO_INCREMENT = 122641 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
-- ----------------------------
-- Records of system_oauth2_access_token
@@ -4670,7 +4706,7 @@ CREATE TABLE `system_oauth2_refresh_token` (
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_refresh_token`(`refresh_token` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 5342 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
) ENGINE = InnoDB AUTO_INCREMENT = 5633 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
-- ----------------------------
-- Records of system_oauth2_refresh_token
@@ -4796,7 +4832,7 @@ CREATE TABLE `system_role_menu` (
`tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
PRIMARY KEY (`id`) USING BTREE,
INDEX `idx_role_id`(`role_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9087 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
) ENGINE = InnoDB AUTO_INCREMENT = 9082 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
-- ----------------------------
-- Records of system_role_menu
@@ -6376,102 +6412,6 @@ INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_t
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8951, 2, 12731, 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 16:13:58', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8952, 1, 12730, 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 16:13:58', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8953, 1, 12731, 'admin', '2026-08-27 16:13:58', 'admin', '2026-08-27 16:13:58', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8959, 2, 12400, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8960, 1, 12400, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8961, 2, 12410, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8962, 1, 12410, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8963, 2, 12420, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8964, 1, 12420, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8965, 2, 12430, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8966, 1, 12430, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8967, 2, 12440, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8968, 1, 12440, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8969, 2, 12441, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8970, 1, 12441, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8971, 2, 12442, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8972, 1, 12442, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8973, 2, 12443, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8974, 1, 12443, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8975, 2, 12450, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8976, 1, 12450, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8977, 2, 12461, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8978, 1, 12461, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8979, 2, 12462, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8980, 1, 12462, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8981, 2, 12463, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8982, 1, 12463, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8983, 2, 12464, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8984, 1, 12464, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8985, 2, 12465, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8986, 1, 12465, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8987, 2, 12466, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8988, 1, 12466, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8989, 2, 12470, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8990, 1, 12470, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8991, 2, 12471, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8992, 1, 12471, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8993, 2, 12472, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8994, 1, 12472, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8995, 2, 12473, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8996, 1, 12473, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8997, 2, 12474, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8998, 1, 12474, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8999, 2, 12480, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9000, 1, 12480, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9001, 2, 12481, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9002, 1, 12481, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9003, 2, 12482, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9004, 1, 12482, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9005, 2, 12483, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9006, 1, 12483, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9007, 2, 12484, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9008, 1, 12484, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9009, 2, 12485, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9010, 1, 12485, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9011, 2, 12486, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9012, 1, 12486, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9013, 2, 12487, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9014, 1, 12487, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9015, 2, 12488, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9016, 1, 12488, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9017, 2, 12489, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9018, 1, 12489, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9019, 2, 12490, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9020, 1, 12490, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9022, 1, 12526, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9023, 2, 12526, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9025, 1, 12234, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9026, 1, 12233, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9027, 2, 12234, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9028, 2, 12233, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9032, 1, 12564, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9033, 1, 12563, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9034, 1, 12562, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9035, 1, 12561, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9036, 1, 12560, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9037, 2, 12564, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9038, 2, 12563, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9039, 2, 12562, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9040, 2, 12561, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9041, 2, 12560, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9047, 2, 12540, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9048, 1, 12540, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9050, 1, 12505, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9051, 1, 12504, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9052, 1, 12503, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9053, 1, 12502, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9054, 1, 12501, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9055, 1, 12500, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9056, 2, 12505, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9057, 2, 12504, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9058, 2, 12503, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9059, 2, 12502, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9060, 2, 12501, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9061, 2, 12500, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9065, 2, 12016, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9066, 1, 12016, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9067, 2, 12081, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9068, 1, 12081, 'admin', '2026-08-27 22:01:14', 'admin', '2026-08-27 22:01:14', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9072, 1, 12554, 'admin', '2026-08-27 22:06:45', 'admin', '2026-08-27 22:06:45', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9073, 1, 12553, 'admin', '2026-08-27 22:06:45', 'admin', '2026-08-27 22:06:45', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9074, 1, 12552, 'admin', '2026-08-27 22:06:45', 'admin', '2026-08-27 22:06:45', b'0', 1);
@@ -6900,7 +6840,7 @@ CREATE TABLE `system_users` (
-- Records of system_users
-- ----------------------------
BEGIN;
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$04$.vd8nPeLwxt6hnSzmAoAyul8BOLX7Cib6QhcxRe30rfvrIPQHH1OG', '芋道源码', '管理员', 103, '[1,2]', '13aoteman@126.com', '18818260272', 1, 'http://test.yudao.iocoder.cn/20260517/blob_1778998103688.png', 0, '127.0.0.1', '2026-08-30 17:05:41', 'admin', '2021-01-05 17:03:47', NULL, '2026-08-30 17:05:41', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1, 'admin', '$2a$04$.vd8nPeLwxt6hnSzmAoAyul8BOLX7Cib6QhcxRe30rfvrIPQHH1OG', '芋道源码', '管理员', 103, '[1,2]', '13aoteman@126.com', '18818260272', 1, 'http://test.yudao.iocoder.cn/20260517/blob_1778998103688.png', 0, '127.0.0.1', '2026-09-04 08:07:41', 'admin', '2021-01-05 17:03:47', NULL, '2026-09-04 08:07:41', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$.vd8nPeLwxt6hnSzmAoAyul8BOLX7Cib6QhcxRe30rfvrIPQHH1OG', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '127.0.0.1', '2026-08-19 17:47:34', '', '2021-01-07 09:07:17', NULL, '2026-08-19 17:47:34', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (103, 'yuanma', '$2a$04$k/d6mc0nySN0i2udwcI8Ee8V5aM5OHixBRbQfXmPuFTUl3Zf/DBs.', '源码', NULL, 106, NULL, 'yuanma@iocoder.cn', '15601701300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2026-04-27 13:19:27', '', '2021-01-13 23:50:35', NULL, '2026-04-27 13:19:27', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (104, 'test', '$2a$04$DQ.n8D3cWRJDhZefFG6ZCOedzpfaLxeFmDrjFXhY.qlgb3Wvbhofm', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, NULL, 0, '127.0.0.1', '2026-08-01 11:54:49', '', '2021-01-21 02:13:53', NULL, '2026-08-01 11:54:49', b'0', 1);
@@ -10,6 +10,8 @@ import cn.iocoder.yudao.framework.common.util.collection.ArrayUtils;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.time.*;
import java.time.format.DateTimeFormatter;
@@ -32,6 +34,42 @@ import static cn.hutool.core.date.DatePattern.*;
*/
public class LocalDateTimeUtils {
/**
* 按经过的时长计算天数,以 24 小时为一天,按指定精度四舍五入
*
* @param beginTime 开始时间
* @param endTime 结束时间
* @param scale 保留的小数位数
* @return 按毫秒精度计算的天数,时间缺失时返回 null
*/
public static BigDecimal getDaysBetween(LocalDateTime beginTime, LocalDateTime endTime, int scale) {
if (beginTime == null || endTime == null) {
return null;
}
return BigDecimal.valueOf(Duration.between(beginTime, endTime).toMillis())
.divide(BigDecimal.valueOf(Duration.ofDays(1).toMillis()), scale, RoundingMode.HALF_UP);
}
/**
* 按经过的时长计算天数,以 24 小时为一天,结果向上取整
*
* 本方法按经过的时长计算,不按自然日期计数。
* 例如跨午夜的 2 小时为 1 天,恰好 24 小时为 1 天,相同时刻为 0 天。
*
* @param beginTime 开始时间
* @param endTime 结束时间
* @return 按毫秒精度计算的天数,时间缺失时返回 null;反向区间按相同规则向上取整
*/
public static Integer getDaysBetweenCeiling(LocalDateTime beginTime, LocalDateTime endTime) {
if (beginTime == null || endTime == null) {
return null;
}
return BigDecimal.valueOf(Duration.between(beginTime, endTime).toMillis())
.divide(BigDecimal.valueOf(Duration.ofDays(1).toMillis()), 0, RoundingMode.CEILING).intValueExact();
}
/**
* 空的 LocalDateTime 对象,主要用于 DB 唯一索引的默认值
*/
@@ -543,6 +581,16 @@ public class LocalDateTimeUtils {
return date == null ? null : date.atTime(LocalTime.MAX);
}
/**
* 获得指定时间所在日期的结束时间
*
* @param time 时间
* @return 当日结束时间;时间为空时返回 {@code null}
*/
public static LocalDateTime getDayEndTime(LocalDateTime time) {
return time == null ? null : getDayEndTime(time.toLocalDate());
}
/**
* 获得自然日期范围对应的日期时间闭区间
*
@@ -36,6 +36,18 @@ public class PageUtils {
* @return 分页结果
*/
public static <T> PageResult<T> buildPageResult(List<T> list, PageParam pageParam) {
return buildPageResult(pageParam, list);
}
/**
* 对内存列表分页,不改变列表顺序
*
* @param pageParam 分页参数,支持 {@link PageParam#PAGE_SIZE_NONE} 返回全部数据
* @param list 已排序的完整列表
* @param <T> 数据类型
* @return 分页结果,超出末页时保留总数并返回空列表
*/
public static <T> PageResult<T> buildPageResult(PageParam pageParam, List<T> list) {
if (CollUtil.isEmpty(list)) {
return PageResult.empty();
}
@@ -43,8 +55,9 @@ public class PageUtils {
if (PageParam.PAGE_SIZE_NONE.equals(pageParam.getPageSize())) {
return new PageResult<>(list, total);
}
int fromIndex = Math.toIntExact(Math.min(getStart(pageParam), total));
int toIndex = Math.toIntExact(Math.min((long) fromIndex + pageParam.getPageSize(), total));
long offset = (long) (pageParam.getPageNo() - 1) * pageParam.getPageSize();
int fromIndex = (int) Math.min(offset, total);
int toIndex = (int) Math.min((long) fromIndex + pageParam.getPageSize(), total);
return new PageResult<>(list.subList(fromIndex, toIndex), total);
}
@@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils.TimeRange;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
@@ -25,6 +26,42 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
*/
public class LocalDateTimeUtilsTest {
@Test
public void testGetDaysBetween() {
// 准备参数
LocalDateTime beginTime = LocalDateTime.of(2026, 12, 31, 23, 0);
// 调用,并断言:按时长四舍五入,跨日不改变计算规则
assertEquals(new BigDecimal("0.0"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime, 1));
assertEquals(new BigDecimal("0.5"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusHours(12), 1));
assertEquals(new BigDecimal("0.1"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusMinutes(72), 1));
assertEquals(new BigDecimal("0.0"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusMinutes(71), 1));
assertEquals(new BigDecimal("1.1"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusHours(26), 1));
assertEquals(new BigDecimal("-0.5"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.minusHours(12), 1));
assertEquals(new BigDecimal("0.50"), LocalDateTimeUtils.getDaysBetween(beginTime, beginTime.plusHours(12), 2));
assertNull(LocalDateTimeUtils.getDaysBetween(null, beginTime, 1));
assertNull(LocalDateTimeUtils.getDaysBetween(beginTime, null, 1));
}
@Test
public void testGetDaysBetweenCeiling() {
// 准备参数
LocalDateTime beginTime = LocalDateTime.of(2026, 12, 31, 23, 0);
// 调用,并断言:按时长取整,不因跨午夜额外增加一天
assertEquals(0, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime));
assertEquals(1, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.plusHours(2)));
assertEquals(1, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.plusDays(1)));
assertEquals(2, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.plusDays(1).plusNanos(1_000_000)));
assertEquals(2, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.plusHours(26)));
assertEquals(0, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.minusHours(2)));
assertEquals(-1, LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, beginTime.minusHours(26)));
assertNull(LocalDateTimeUtils.getDaysBetweenCeiling(null, beginTime));
assertNull(LocalDateTimeUtils.getDaysBetweenCeiling(beginTime, null));
}
@Test
public void testDateCompare() {
// 准备参数
@@ -0,0 +1,68 @@
package cn.iocoder.yudao.framework.common.util.object;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
/**
* {@link PageUtils} 的单元测试
*/
public class PageUtilsTest {
@Test
public void testBuildPageResult() {
// 准备参数
PageParam pageParam = new PageParam();
pageParam.setPageSize(2);
List<Integer> list = Arrays.asList(1, 2, 3);
// 调用并断言首页和末页
PageResult<Integer> result = PageUtils.buildPageResult(pageParam, list);
assertEquals(3L, result.getTotal());
assertEquals(Arrays.asList(1, 2), result.getList());
pageParam.setPageNo(2);
result = PageUtils.buildPageResult(pageParam, list);
assertEquals(3L, result.getTotal());
assertEquals(Collections.singletonList(3), result.getList());
assertEquals(Arrays.asList(1, 2, 3), list);
}
@Test
public void testBuildPageResult_empty() {
PageParam pageParam = new PageParam();
assertEquals(0L, PageUtils.buildPageResult(pageParam, Collections.emptyList()).getTotal());
assertTrue(PageUtils.buildPageResult(pageParam, null).getList().isEmpty());
}
@Test
public void testBuildPageResult_pastLastPage() {
PageParam pageParam = new PageParam();
pageParam.setPageNo(Integer.MAX_VALUE);
pageParam.setPageSize(200);
PageResult<Integer> result = PageUtils.buildPageResult(pageParam, Arrays.asList(1, 2, 3));
assertEquals(3L, result.getTotal());
assertTrue(result.getList().isEmpty());
}
@Test
public void testBuildPageResult_noPagination() {
PageParam pageParam = new PageParam();
pageParam.setPageNo(2);
pageParam.setPageSize(PageParam.PAGE_SIZE_NONE);
PageResult<Integer> result = PageUtils.buildPageResult(pageParam, Arrays.asList(1, 2, 3));
assertEquals(3L, result.getTotal());
assertEquals(Arrays.asList(1, 2, 3), result.getList());
}
}
@@ -237,6 +237,13 @@ spring:
- Path=/admin-api/pms/**
filters:
- RewritePath=/admin-api/pms/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
## oa-server 服务
- id: oa-admin-api # 路由的编号
uri: grayLb://oa-server
predicates:
- Path=/admin-api/oa/**
filters:
- RewritePath=/admin-api/oa/v3/api-docs, /v3/api-docs
x-forwarded:
prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀
default-filters:
@@ -314,6 +321,9 @@ knife4j:
- name: pms-server
service-name: pms-server
url: /admin-api/pms/v3/api-docs
- name: oa-server
service-name: oa-server
url: /admin-api/oa/v3/api-docs
--- #################### 芋道相关配置 ####################
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 请假申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaLeaveApplyStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_leave_apply";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/leave-apply/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 会议室预定审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaMeetingRoomBookingStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_meeting_room_booking";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/meeting-room-booking/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 公文收文审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaOfficialDocReceiveStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_official_doc_receive";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/official-doc-receive/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 公文发文审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaOfficialDocSendStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_official_doc_send";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/official-doc-send/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 加班申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaOvertimeApplyStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_overtime_apply";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/overtime-apply/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 转正申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaRegularApplyStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_regular_apply";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/regular-apply/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 费用报销审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaReimbursementStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_reimbursement";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/reimbursement/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 离职申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaResignApplyStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_resign_apply";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/resign-apply/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 用印申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaSealApplyStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_seal_apply";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/seal-apply/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 用品申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaSupplyApplyStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_supply_apply";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/supply-apply/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 出差申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaTravelApplyStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_travel_apply";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/travel-apply/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 出差报销审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaTravelReimbursementStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_travel_reimbursement";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/travel-reimbursement/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 用车申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaVehicleApplyStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_vehicle_apply";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/vehicle-apply/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.bpm.api.event;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.client.RestTemplate;
/**
* 还车申请审批结果的 HTTP 转发监听器
*
* @author 芋道源码
*/
public class OaVehicleReturnStatusListener extends BpmProcessInstanceStatusEventListener {
@Resource
private RestTemplate loadBalancedRestTemplate;
@Override
public String getProcessDefinitionKey() {
return "oa_vehicle_return";
}
@Override
public void onEvent(@RequestBody @Valid BpmProcessInstanceStatusEvent event) {
BpmHttpRequestUtils.executeBpmHttpRequest(event,
"http://oa-server/rpc-api/oa/vehicle-return/update-audit-status",
loadBalancedRestTemplate);
}
}
@@ -2,6 +2,20 @@ package cn.iocoder.yudao.module.bpm.framework.rpc.config;
import cn.iocoder.yudao.module.bpm.api.event.CrmContractStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.CrmReceivableStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaLeaveApplyStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaMeetingRoomBookingStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaOfficialDocReceiveStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaOfficialDocSendStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaOvertimeApplyStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaRegularApplyStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaReimbursementStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaResignApplyStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaSealApplyStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaSupplyApplyStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaTravelApplyStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaTravelReimbursementStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaVehicleApplyStatusListener;
import cn.iocoder.yudao.module.bpm.api.event.OaVehicleReturnStatusListener;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.PostApi;
import cn.iocoder.yudao.module.system.api.dict.DictDataApi;
@@ -33,4 +47,88 @@ public class RpcConfiguration {
return new CrmContractStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaLeaveApplyStatusListener")
public OaLeaveApplyStatusListener oaLeaveApplyStatusListener() {
return new OaLeaveApplyStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaMeetingRoomBookingStatusListener")
public OaMeetingRoomBookingStatusListener oaMeetingRoomBookingStatusListener() {
return new OaMeetingRoomBookingStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaOfficialDocReceiveStatusListener")
public OaOfficialDocReceiveStatusListener oaOfficialDocReceiveStatusListener() {
return new OaOfficialDocReceiveStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaOfficialDocSendStatusListener")
public OaOfficialDocSendStatusListener oaOfficialDocSendStatusListener() {
return new OaOfficialDocSendStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaOvertimeApplyStatusListener")
public OaOvertimeApplyStatusListener oaOvertimeApplyStatusListener() {
return new OaOvertimeApplyStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaRegularApplyStatusListener")
public OaRegularApplyStatusListener oaRegularApplyStatusListener() {
return new OaRegularApplyStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaReimbursementStatusListener")
public OaReimbursementStatusListener oaReimbursementStatusListener() {
return new OaReimbursementStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaResignApplyStatusListener")
public OaResignApplyStatusListener oaResignApplyStatusListener() {
return new OaResignApplyStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaSealApplyStatusListener")
public OaSealApplyStatusListener oaSealApplyStatusListener() {
return new OaSealApplyStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaSupplyApplyStatusListener")
public OaSupplyApplyStatusListener oaSupplyApplyStatusListener() {
return new OaSupplyApplyStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaTravelApplyStatusListener")
public OaTravelApplyStatusListener oaTravelApplyStatusListener() {
return new OaTravelApplyStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaTravelReimbursementStatusListener")
public OaTravelReimbursementStatusListener oaTravelReimbursementStatusListener() {
return new OaTravelReimbursementStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaVehicleApplyStatusListener")
public OaVehicleApplyStatusListener oaVehicleApplyStatusListener() {
return new OaVehicleApplyStatusListener();
}
@Bean
@ConditionalOnMissingBean(name = "oaVehicleReturnStatusListener")
public OaVehicleReturnStatusListener oaVehicleReturnStatusListener() {
return new OaVehicleReturnStatusListener();
}
}
+24
View File
@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<modules>
<module>yudao-module-oa-api</module>
<module>yudao-module-oa-server</module>
</modules>
<artifactId>yudao-module-oa</artifactId>
<packaging>pom</packaging>
<name>${project.artifactId}</name>
<description>
oa 包下,办公自动化(Office Automation)。
例如说:日程、工作计划、笔记、通讯录、内部邮件和讨论区等等
</description>
</project>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-oa</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-oa-api</artifactId>
<packaging>jar</packaging>
<name>${project.artifactId}</name>
<description>oa 模块 API,暴露给其它模块调用</description>
<dependencies>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-common</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
<optional>true</optional>
</dependency>
</dependencies>
</project>
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.oa.enums;
import cn.iocoder.yudao.framework.common.enums.RpcConstants;
/**
* API 相关的枚举
*
* @author 芋道源码
*/
public class ApiConstants {
/** 服务名,需要和 spring.application.name 保持一致。 */
public static final String NAME = "oa-server";
public static final String PREFIX = RpcConstants.RPC_API_PREFIX + "/oa";
public static final String VERSION = "1.0.0";
}
@@ -0,0 +1,148 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-oa</artifactId>
<version>${revision}</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>yudao-module-oa-server</artifactId>
<name>${project.artifactId}</name>
<description>
oa 包下,办公自动化(Office Automation)。
例如说:日程、工作计划、笔记、通讯录、内部邮件和讨论区等等
</description>
<dependencies>
<!-- Spring Cloud 基础 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-env</artifactId>
</dependency>
<!-- 依赖服务 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-oa-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-system-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-bpm-api</artifactId>
<version>${revision}</version>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-module-infra-api</artifactId>
<version>${revision}</version>
</dependency>
<!-- 企业邮箱协议和文本处理 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-mail</artifactId>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
</dependency>
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
</dependency>
<!-- 业务组件 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-biz-ip</artifactId>
</dependency>
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-biz-tenant</artifactId>
</dependency>
<!-- Web 相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-security</artifactId>
</dependency>
<!-- DB 相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
</dependency>
<!-- RPC 远程调用相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-rpc</artifactId>
</dependency>
<!-- Registry 注册中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- Config 配置中心相关 -->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- Job 定时任务相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-job</artifactId>
</dependency>
<!-- 工具类相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-excel</artifactId>
</dependency>
<!-- 监控相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-monitor</artifactId>
</dependency>
<!-- Test 测试相关 -->
<dependency>
<groupId>cn.iocoder.cloud</groupId>
<artifactId>yudao-spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<!-- 设置构建的 jar 包名 -->
<finalName>${project.artifactId}</finalName>
<plugins>
<!-- 打包 -->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>${spring.boot.version}</version>
<executions>
<execution>
<goals>
<goal>repackage</goal> <!-- 将引入的 jar 打入其中 -->
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
@@ -0,0 +1,12 @@
package cn.iocoder.yudao.module.oa;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/** OA 模块独立启动入口。 */
@SpringBootApplication
public class OaServerApplication {
public static void main(String[] args) {
SpringApplication.run(OaServerApplication.class, args);
}
}
@@ -0,0 +1,214 @@
package cn.iocoder.yudao.module.oa.controller.admin.announcement;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.announcement.vo.OaAnnouncementPageReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.announcement.vo.OaAnnouncementRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.announcement.vo.OaAnnouncementSaveReqVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.announcement.OaAnnouncementDO;
import cn.iocoder.yudao.module.oa.dal.dataobject.announcement.OaAnnouncementReceiverDO;
import cn.iocoder.yudao.module.oa.service.announcement.OaAnnouncementService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - OA 公告")
@RestController
@RequestMapping("/oa/announcement")
@Validated
public class OaAnnouncementController {
@Resource
private OaAnnouncementService announcementService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
// ==================== 我发布的 ====================
@PostMapping("/create")
@Operation(summary = "创建公告")
@PreAuthorize("@ss.hasPermission('oa:announcement:create')")
public CommonResult<Long> createAnnouncement(@Valid @RequestBody OaAnnouncementSaveReqVO createReqVO) {
return success(announcementService.createAnnouncement(createReqVO, getLoginUserId()));
}
@PutMapping("/update")
@Operation(summary = "更新公告")
@PreAuthorize("@ss.hasPermission('oa:announcement:update')")
public CommonResult<Boolean> updateAnnouncement(@Valid @RequestBody OaAnnouncementSaveReqVO updateReqVO) {
announcementService.updateAnnouncement(updateReqVO, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除发布的公告")
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:announcement:delete')")
public CommonResult<Boolean> deleteAnnouncement(@RequestParam("id") Long id) {
announcementService.deleteAnnouncement(id, getLoginUserId());
return success(true);
}
@GetMapping("/published-page")
@Operation(summary = "获得我发布的公告分页")
@PreAuthorize("@ss.hasPermission('oa:announcement:query')")
public CommonResult<PageResult<OaAnnouncementRespVO>> getPublishedAnnouncementPage(
@Valid OaAnnouncementPageReqVO pageReqVO) {
Long userId = getLoginUserId();
PageResult<OaAnnouncementDO> pageResult = announcementService
.getPublishedAnnouncementPage(pageReqVO, userId);
return success(new PageResult<>(buildAnnouncementRespVOList(pageResult.getList(), userId),
pageResult.getTotal()));
}
// ==================== 我收到的 ====================
@DeleteMapping("/delete-received")
@Operation(summary = "删除接收的公告")
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
public CommonResult<Boolean> deleteReceivedAnnouncement(@RequestParam("id") Long id) {
announcementService.deleteReceivedAnnouncement(id, getLoginUserId());
return success(true);
}
@GetMapping("/received-page")
@Operation(summary = "获得接收的公告分页")
@PreAuthorize("@ss.hasPermission('oa:announcement:query')")
public CommonResult<PageResult<OaAnnouncementRespVO>> getReceivedAnnouncementPage(
@Valid OaAnnouncementPageReqVO pageReqVO) {
Long userId = getLoginUserId();
PageResult<OaAnnouncementDO> pageResult = announcementService
.getReceivedAnnouncementPage(pageReqVO, userId);
return success(new PageResult<>(buildAnnouncementRespVOList(pageResult.getList(), userId),
pageResult.getTotal()));
}
@PutMapping("/update-read-status")
@Operation(summary = "标记公告为已读")
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
public CommonResult<Boolean> updateAnnouncementReadStatus(@RequestParam("id") Long id) {
announcementService.updateAnnouncementReadStatus(id, getLoginUserId());
return success(true);
}
@PostMapping("/forward")
@Operation(summary = "转发公告给下属")
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
public CommonResult<Integer> forwardAnnouncement(@RequestParam("id") Long id) {
return success(announcementService.forwardAnnouncement(id, getLoginUserId()));
}
// ==================== 公共详情 ====================
@GetMapping("/get")
@Operation(summary = "获得公告详情")
@Parameter(name = "id", description = "公告编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:announcement:query')")
public CommonResult<OaAnnouncementRespVO> getAnnouncement(@RequestParam("id") Long id) {
Long userId = getLoginUserId();
OaAnnouncementDO announcement = announcementService.getAnnouncement(id, userId);
return success(buildAnnouncementRespVO(announcement, userId));
}
// ==================== 拼接 VO ====================
/**
* 拼接公告详情
*
* @param announcement 公告
* @param loginUserId 当前登录用户编号
* @return 公告响应
*/
private OaAnnouncementRespVO buildAnnouncementRespVO(OaAnnouncementDO announcement, Long loginUserId) {
if (announcement == null) {
return null;
}
return CollUtil.getFirst(buildAnnouncementRespVOList(Collections.singletonList(announcement), loginUserId));
}
/**
* 拼接公告响应列表
*
* @param announcements 公告列表
* @param loginUserId 当前登录用户编号
* @return 公告响应列表
*/
private List<OaAnnouncementRespVO> buildAnnouncementRespVOList(List<OaAnnouncementDO> announcements,
Long loginUserId) {
if (CollUtil.isEmpty(announcements)) {
return Collections.emptyList();
}
// 1. 查询公告接收关系、发布人、接收人和发布人部门信息
Map<Long, List<OaAnnouncementReceiverDO>> receiverListMap = announcementService
.getAnnouncementReceiverListMap(convertSet(announcements, OaAnnouncementDO::getId));
Collection<Long> userIds = new ArrayList<>(convertSet(announcements,
announcement -> NumberUtils.parseLong(announcement.getCreator())));
receiverListMap.values().forEach(receivers -> userIds.addAll(
convertSet(receivers, OaAnnouncementReceiverDO::getReceiverUserId)));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
Set<Long> subordinateUserIds = convertSet(adminUserApi.getUserListBySubordinate(loginUserId).getCheckedData(), AdminUserRespDTO::getId);
// 2. 拼接公告、发布人、接收人和当前接收状态
return convertList(announcements, announcementDO -> {
OaAnnouncementRespVO announcement = BeanUtils.toBean(announcementDO, OaAnnouncementRespVO.class)
.setPublisherUserId(NumberUtils.parseLong(announcementDO.getCreator()));
MapUtils.findAndThen(userMap, announcement.getPublisherUserId(), publisher -> {
announcement.setPublisherUserName(publisher.getNickname());
announcement.setPublisherDeptId(publisher.getDeptId());
MapUtils.findAndThen(deptMap, publisher.getDeptId(),
dept -> announcement.setPublisherDeptName(dept.getName()));
});
List<OaAnnouncementReceiverDO> receivers = receiverListMap.getOrDefault(announcement.getId(),
Collections.emptyList());
List<Long> receiverUserIds = convertList(receivers, OaAnnouncementReceiverDO::getReceiverUserId);
announcement.setReceiverUserIds(receiverUserIds);
announcement.setReceiverUserNames(convertList(receiverUserIds, receiverUserId -> {
AdminUserRespDTO receiverUser = userMap.get(receiverUserId);
return receiverUser != null ? receiverUser.getNickname() : null;
}));
OaAnnouncementReceiverDO loginUserReceiver = CollUtil.findOne(receivers,
receiver -> receiver.getReceiverUserId().equals(loginUserId));
if (loginUserReceiver != null) {
announcement.setReadStatus(loginUserReceiver.getReadStatus());
}
announcement.setForwarded(CollUtil.containsAny(receiverUserIds, subordinateUserIds));
return announcement;
});
}
}
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.oa.controller.admin.announcement.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.announcement.OaAnnouncementTypeEnum;
import cn.iocoder.yudao.module.oa.enums.schedule.OaPriorityEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - OA 公告分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaAnnouncementPageReqVO extends PageParam {
@Schema(description = "公告标题,模糊匹配", example = "国庆节放假通知")
private String title;
@Schema(description = "公告类型", example = "1")
@InEnum(value = OaAnnouncementTypeEnum.class, message = "公告类型必须是 {value}")
private Integer type;
@Schema(description = "优先级", example = "1")
@InEnum(value = OaPriorityEnum.class, message = "优先级必须是 {value}")
private Integer priority;
@Schema(description = "是否已读", example = "false")
private Boolean readStatus;
@Schema(description = "发布时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
@@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.oa.controller.admin.announcement.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - OA 公告 Response VO")
@Data
public class OaAnnouncementRespVO {
@Schema(description = "公告编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "发布人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long publisherUserId;
@Schema(description = "发布人用户昵称", example = "芋道源码")
private String publisherUserName;
@Schema(description = "发布人部门编号", example = "1")
private Long publisherDeptId;
@Schema(description = "发布人部门名称", example = "研发部")
private String publisherDeptName;
@Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type;
@Schema(description = "优先级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer priority;
@Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "国庆节放假通知")
private String title;
@Schema(description = "公告内容", example = "请各部门提前安排好假期值班工作。")
private String content;
@Schema(description = "相关链接", example = "https://example.com/file.pdf")
private String url;
@Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean top;
@Schema(description = "接收人用户编号列表", example = "[1, 2]")
private List<Long> receiverUserIds;
@Schema(description = "接收人用户昵称列表", example = "[\"张三\", \"李四\"]")
private List<String> receiverUserNames;
@Schema(description = "当前接收人是否已读", example = "false")
private Boolean readStatus;
@Schema(description = "当前接收人是否已转发给下属", example = "false")
private Boolean forwarded;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime createTime;
}
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.oa.controller.admin.announcement.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.announcement.OaAnnouncementTypeEnum;
import cn.iocoder.yudao.module.oa.enums.schedule.OaPriorityEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - OA 公告新增/修改 Request VO")
@Data
public class OaAnnouncementSaveReqVO {
@Schema(description = "公告编号", example = "1024")
private Long id;
@Schema(description = "公告类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "公告类型不能为空")
@InEnum(value = OaAnnouncementTypeEnum.class, message = "公告类型必须是 {value}")
private Integer type;
@Schema(description = "优先级", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "优先级不能为空")
@InEnum(value = OaPriorityEnum.class, message = "优先级必须是 {value}")
private Integer priority;
@Schema(description = "公告标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "国庆节放假通知")
@NotBlank(message = "公告标题不能为空")
@Size(max = 255, message = "公告标题长度不能超过 255 个字符")
private String title;
@Schema(description = "公告内容", example = "请各部门提前安排好假期值班工作。")
private String content;
@Schema(description = "相关链接", example = "https://www.iocoder.cn")
@Size(max = 512, message = "相关链接长度不能超过 512 个字符")
private String url;
@Schema(description = "是否置顶", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否置顶不能为空")
private Boolean top;
}
@@ -0,0 +1,260 @@
package cn.iocoder.yudao.module.oa.controller.admin.attendance;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceMonthReportReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceMonthReportRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendancePageReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceUpdateReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceWeekReportReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.attendance.vo.OaAttendanceWeekReportRespVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.attendance.OaAttendanceDO;
import cn.iocoder.yudao.module.oa.service.attendance.OaAttendanceService;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.servlet.ServletUtils.getClientIP;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - OA 考勤")
@RestController
@RequestMapping("/oa/attendance")
@Validated
public class OaAttendanceController {
@Resource
private OaAttendanceService attendanceService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
// ==================== 我的考勤 ====================
@PostMapping("/clock")
@Operation(summary = "执行当前用户打卡")
public CommonResult<Long> clockAttendance() {
return success(attendanceService.clockAttendance(getLoginUserId(), getClientIP()));
}
@GetMapping("/my-page")
@Operation(summary = "获得我的考勤分页列表")
public CommonResult<PageResult<OaAttendanceRespVO>> getMyAttendancePage(
@Valid OaAttendancePageReqVO pageReqVO) {
PageResult<OaAttendanceDO> pageResult = attendanceService.getMyAttendancePage(
pageReqVO, getLoginUserId());
return success(new PageResult<>(buildAttendanceRespVOList(pageResult.getList()), pageResult.getTotal()));
}
@GetMapping("/my-today-list")
@Operation(summary = "获得我的今日考勤列表")
public CommonResult<List<OaAttendanceRespVO>> getMyTodayAttendanceList() {
return success(buildAttendanceRespVOList(
attendanceService.getTodayAttendanceList(getLoginUserId())));
}
// ==================== 考勤管理 ====================
@PutMapping("/update")
@Operation(summary = "更新考勤记录")
@PreAuthorize("@ss.hasPermission('oa:attendance:update')")
public CommonResult<Boolean> updateAttendance(@Valid @RequestBody OaAttendanceUpdateReqVO updateReqVO) {
attendanceService.updateAttendance(updateReqVO, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除考勤记录")
@Parameter(name = "id", description = "考勤记录编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:attendance:delete')")
public CommonResult<Boolean> deleteAttendance(@RequestParam("id") Long id) {
attendanceService.deleteAttendance(id, getLoginUserId());
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得考勤详情")
@Parameter(name = "id", description = "考勤记录编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:attendance:query')")
public CommonResult<OaAttendanceRespVO> getAttendance(@RequestParam("id") Long id) {
OaAttendanceDO attendance = attendanceService.getAttendance(id, getLoginUserId());
return success(buildAttendanceRespVO(attendance));
}
@GetMapping("/page")
@Operation(summary = "获得考勤分页列表")
@PreAuthorize("@ss.hasPermission('oa:attendance:query')")
public CommonResult<PageResult<OaAttendanceRespVO>> getAttendancePage(
@Valid OaAttendancePageReqVO pageReqVO) {
PageResult<OaAttendanceDO> pageResult = attendanceService.getAttendancePage(pageReqVO, getLoginUserId());
return success(new PageResult<>(buildAttendanceRespVOList(pageResult.getList()), pageResult.getTotal()));
}
// ==================== 考勤报表 ====================
@GetMapping("/week-report")
@Operation(summary = "获得考勤周报")
@PreAuthorize("@ss.hasPermission('oa:attendance:query')")
public CommonResult<List<OaAttendanceWeekReportRespVO>> getAttendanceWeekReport(
@Valid OaAttendanceWeekReportReqVO reqVO) {
return success(buildAttendanceWeekReportRespVOList(
attendanceService.getAttendanceWeekReport(reqVO, getLoginUserId())));
}
@GetMapping("/month-report")
@Operation(summary = "获得考勤月报")
@PreAuthorize("@ss.hasPermission('oa:attendance:query')")
public CommonResult<List<OaAttendanceMonthReportRespVO>> getAttendanceMonthReport(
@Valid OaAttendanceMonthReportReqVO reqVO) {
return success(buildAttendanceMonthReportRespVOList(
attendanceService.getAttendanceMonthReport(reqVO, getLoginUserId())));
}
// ==================== 拼接 VO ====================
/**
* 拼接考勤详情
*
* @param attendance 考勤记录
* @return 考勤响应
*/
private OaAttendanceRespVO buildAttendanceRespVO(OaAttendanceDO attendance) {
if (attendance == null) {
return null;
}
return CollUtil.getFirst(buildAttendanceRespVOList(Collections.singletonList(attendance)));
}
/**
* 拼接考勤响应列表
*
* @param attendances 考勤记录列表
* @return 考勤响应列表
*/
private List<OaAttendanceRespVO> buildAttendanceRespVOList(List<OaAttendanceDO> attendances) {
if (CollUtil.isEmpty(attendances)) {
return Collections.emptyList();
}
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(attendances, OaAttendanceDO::getUserId));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
return BeanUtils.toBean(attendances, OaAttendanceRespVO.class, attendance ->
fillAttendanceUserInfo(attendance, userMap, deptMap));
}
/**
* 拼接考勤周报响应列表
*
* @param reports 考勤周报列表
* @return 包含用户和部门信息的考勤周报列表
*/
private List<OaAttendanceWeekReportRespVO> buildAttendanceWeekReportRespVOList(
List<OaAttendanceWeekReportRespVO> reports) {
if (CollUtil.isEmpty(reports)) {
return Collections.emptyList();
}
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(reports, OaAttendanceWeekReportRespVO::getUserId));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
reports.forEach(report -> fillAttendanceUserInfo(report, userMap, deptMap));
return reports;
}
/**
* 拼接考勤月报响应列表
*
* @param reports 考勤月报列表
* @return 包含用户和部门信息的考勤月报列表
*/
private List<OaAttendanceMonthReportRespVO> buildAttendanceMonthReportRespVOList(
List<OaAttendanceMonthReportRespVO> reports) {
if (CollUtil.isEmpty(reports)) {
return Collections.emptyList();
}
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
convertSet(reports, OaAttendanceMonthReportRespVO::getUserId));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(
convertSet(userMap.values(), AdminUserRespDTO::getDeptId));
reports.forEach(report -> fillAttendanceUserInfo(report, userMap, deptMap));
return reports;
}
/**
* 拼接考勤响应的用户和部门信息
*
* @param attendance 考勤响应
* @param userMap 用户 Map
* @param deptMap 部门 Map
*/
private void fillAttendanceUserInfo(OaAttendanceRespVO attendance,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
MapUtils.findAndThen(userMap, attendance.getUserId(), user -> {
attendance.setUserName(user.getNickname()).setDeptId(user.getDeptId());
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> attendance.setDeptName(dept.getName()));
});
}
/**
* 拼接考勤周报响应的用户和部门信息
*
* @param report 考勤周报响应
* @param userMap 用户 Map
* @param deptMap 部门 Map
*/
private void fillAttendanceUserInfo(OaAttendanceWeekReportRespVO report,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
MapUtils.findAndThen(userMap, report.getUserId(), user -> {
report.setUserName(user.getNickname()).setDeptId(user.getDeptId());
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> report.setDeptName(dept.getName()));
});
}
/**
* 拼接考勤月报响应的用户和部门信息
*
* @param report 考勤月报响应
* @param userMap 用户 Map
* @param deptMap 部门 Map
*/
private void fillAttendanceUserInfo(OaAttendanceMonthReportRespVO report,
Map<Long, AdminUserRespDTO> userMap,
Map<Long, DeptRespDTO> deptMap) {
MapUtils.findAndThen(userMap, report.getUserId(), user -> {
report.setUserName(user.getNickname()).setDeptId(user.getDeptId());
MapUtils.findAndThen(deptMap, user.getDeptId(), dept -> report.setDeptName(dept.getName()));
});
}
}
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - OA 考勤月报 Request VO")
@Data
public class OaAttendanceMonthReportReqVO {
@Schema(description = "年份", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026")
@NotNull(message = "年份不能为空")
@Min(value = 2000, message = "年份不能小于 2000")
private Integer year;
@Schema(description = "月份", requiredMode = Schema.RequiredMode.REQUIRED, example = "9")
@NotNull(message = "月份不能为空")
@Min(value = 1, message = "月份不能小于 1")
@Max(value = 12, message = "月份不能大于 12")
private Integer month;
@Schema(description = "用户编号", example = "1")
private Long userId;
}
@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
@Schema(description = "管理后台 - OA 考勤月报 Response VO")
@Data
@Accessors(chain = true)
public class OaAttendanceMonthReportRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@Schema(description = "用户昵称", example = "芋道")
private String userName;
@Schema(description = "部门编号", example = "100")
private Long deptId;
@Schema(description = "部门名称", example = "研发部")
private String deptName;
@Schema(description = "上班打卡次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
private Integer clockInCount;
@Schema(description = "下班打卡次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "19")
private Integer clockOutCount;
@Schema(description = "正常次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "36")
private Integer normalCount;
@Schema(description = "迟到次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer lateCount;
@Schema(description = "早退次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer earlyCount;
@Schema(description = "请假天数,审批通过且开始时间在当月的申请天数合计", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
private Integer leaveDays;
@Schema(description = "出差天数,审批通过且开始时间在当月的申请天数合计", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
private Integer travelDays;
@Schema(description = "旷工天数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer absentDays;
}
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.attendance.OaAttendanceStatusEnum;
import cn.iocoder.yudao.module.oa.enums.attendance.OaAttendanceTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - OA 考勤分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaAttendancePageReqVO extends PageParam {
@Schema(description = "用户编号", example = "1")
private Long userId;
@Schema(description = "考勤类型", example = "1")
@InEnum(value = OaAttendanceTypeEnum.class, message = "考勤类型必须是 {value}")
private Integer type;
@Schema(description = "考勤状态", example = "1")
@InEnum(value = OaAttendanceStatusEnum.class, message = "考勤状态必须是 {value}")
private Integer status;
@Schema(description = "考勤时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] attendanceTime;
}
@@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - OA 考勤 Response VO")
@Data
@Accessors(chain = true)
public class OaAttendanceRespVO {
@Schema(description = "考勤记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@Schema(description = "用户昵称", example = "芋道")
private String userName;
@Schema(description = "部门编号", example = "100")
private Long deptId;
@Schema(description = "部门名称", example = "研发部")
private String deptName;
@Schema(description = "考勤类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type;
@Schema(description = "考勤状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer status;
@Schema(description = "考勤时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789380000000")
private LocalDateTime attendanceTime;
@Schema(description = "考勤 IP", example = "127.0.0.1")
private String attendanceIp;
@Schema(description = "考勤备注", example = "正常打卡")
private String remark;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime createTime;
}
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.attendance.OaAttendanceStatusEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - OA 考勤修改 Request VO")
@Data
public class OaAttendanceUpdateReqVO {
@Schema(description = "考勤记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "考勤记录编号不能为空")
private Long id;
@Schema(description = "考勤状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "考勤状态不能为空")
@InEnum(value = OaAttendanceStatusEnum.class, message = "考勤状态必须是 {value}")
private Integer status;
@Schema(description = "考勤备注", example = "因交通拥堵迟到")
@Size(max = 500, message = "考勤备注不能超过 500 个字符")
private String remark;
}
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDate;
@Schema(description = "管理后台 - OA 考勤周报 Request VO")
@Data
public class OaAttendanceWeekReportReqVO {
@Schema(description = "周开始日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-09-07")
@NotNull(message = "周开始日期不能为空")
@DateTimeFormat(iso = DateTimeFormat.ISO.DATE)
private LocalDate startDate;
@Schema(description = "用户编号", example = "1")
private Long userId;
}
@@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.oa.controller.admin.attendance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - OA 考勤周报 Response VO")
@Data
@Accessors(chain = true)
public class OaAttendanceWeekReportRespVO {
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@Schema(description = "用户昵称", example = "芋道")
private String userName;
@Schema(description = "部门编号", example = "100")
private Long deptId;
@Schema(description = "部门名称", example = "研发部")
private String deptName;
@Schema(description = "每日考勤列表", requiredMode = Schema.RequiredMode.REQUIRED)
private List<DailyAttendance> dailyAttendances;
@Schema(description = "管理后台 - OA 每日考勤 Response VO")
@Data
@Accessors(chain = true)
public static class DailyAttendance {
@Schema(description = "日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-09-07")
private LocalDate date;
@Schema(description = "上班考勤记录编号", example = "1024")
private Long clockInId;
@Schema(description = "上班打卡时间", type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime clockInTime;
@Schema(description = "上班打卡状态", example = "1")
private Integer clockInStatus;
@Schema(description = "下班考勤记录编号", example = "1025")
private Long clockOutId;
@Schema(description = "下班打卡时间", type = "integer", format = "int64", example = "1789380000000")
private LocalDateTime clockOutTime;
@Schema(description = "下班打卡状态", example = "1")
private Integer clockOutStatus;
}
}
@@ -0,0 +1,88 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.category.OaContactCategoryRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.category.OaContactCategorySaveReqVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.contact.OaContactCategoryDO;
import cn.iocoder.yudao.module.oa.service.contact.OaContactCategoryService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - OA 联系人分类")
@RestController
@RequestMapping("/oa/contact-category")
@Validated
public class OaContactCategoryController {
@Resource
private OaContactCategoryService contactCategoryService;
@PostMapping("/create")
@Operation(summary = "创建联系人分类")
public CommonResult<Long> createContactCategory(
@Valid @RequestBody OaContactCategorySaveReqVO createReqVO) {
return success(contactCategoryService.createContactCategory(createReqVO, getLoginUserId()));
}
@PutMapping("/update")
@Operation(summary = "更新联系人分类")
public CommonResult<Boolean> updateContactCategory(
@Valid @RequestBody OaContactCategorySaveReqVO updateReqVO) {
contactCategoryService.updateContactCategory(updateReqVO, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除联系人分类")
@Parameter(name = "id", description = "分类编号", required = true, example = "1024")
public CommonResult<Boolean> deleteContactCategory(@RequestParam("id") Long id) {
contactCategoryService.deleteContactCategory(id, getLoginUserId());
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得联系人分类")
@Parameter(name = "id", description = "分类编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
public CommonResult<OaContactCategoryRespVO> getContactCategory(@RequestParam("id") Long id) {
OaContactCategoryDO category = contactCategoryService.getContactCategory(id, getLoginUserId());
return success(BeanUtils.toBean(category, OaContactCategoryRespVO.class));
}
@GetMapping("/simple-list")
@Operation(summary = "获得分类精简列表")
public CommonResult<List<OaContactCategoryRespVO>> getSimpleContactCategoryList() {
List<OaContactCategoryDO> categories = contactCategoryService.getContactCategoryList(getLoginUserId());
return success(convertList(categories, category -> new OaContactCategoryRespVO()
.setId(category.getId()).setName(category.getName())));
}
@GetMapping("/list")
@Operation(summary = "获得联系人分类列表")
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
public CommonResult<List<OaContactCategoryRespVO>> getContactCategoryList() {
List<OaContactCategoryDO> categories = contactCategoryService.getContactCategoryList(getLoginUserId());
return success(BeanUtils.toBean(categories, OaContactCategoryRespVO.class));
}
}
@@ -0,0 +1,234 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactHandleReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactPageReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactSaveReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.contact.vo.OaContactShareReqVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.contact.OaContactDO;
import cn.iocoder.yudao.module.oa.dal.dataobject.contact.OaContactCategoryDO;
import cn.iocoder.yudao.module.oa.service.contact.OaContactCategoryService;
import cn.iocoder.yudao.module.oa.dal.dataobject.contact.OaContactShareDO;
import cn.iocoder.yudao.module.oa.service.contact.OaContactService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertListByFlatMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - OA 通讯录")
@RestController
@RequestMapping("/oa/contact")
@Validated
public class OaContactController {
@Resource
private OaContactService contactService;
@Resource
private OaContactCategoryService contactCategoryService;
@Resource
private AdminUserApi adminUserApi;
// ==================== 我的联系人 ====================
@PostMapping("/create")
@Operation(summary = "创建外部联系人")
@PreAuthorize("@ss.hasPermission('oa:contact:create')")
public CommonResult<Long> createContact(@Valid @RequestBody OaContactSaveReqVO createReqVO) {
return success(contactService.createContact(createReqVO, getLoginUserId()));
}
@PutMapping("/update")
@Operation(summary = "更新外部联系人")
@PreAuthorize("@ss.hasPermission('oa:contact:update')")
public CommonResult<Boolean> updateContact(@Valid @RequestBody OaContactSaveReqVO updateReqVO) {
contactService.updateContact(updateReqVO, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除外部联系人")
@Parameter(name = "id", description = "联系人编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:contact:delete')")
public CommonResult<Boolean> deleteContact(@RequestParam("id") Long id) {
contactService.deleteContact(id, getLoginUserId());
return success(true);
}
@PostMapping("/share")
@Operation(summary = "共享外部联系人")
public CommonResult<Boolean> shareContact(@Valid @RequestBody OaContactShareReqVO shareReqVO) {
contactService.shareContact(shareReqVO.getContactId(), shareReqVO.getUserIds(), getLoginUserId());
return success(true);
}
// ==================== 共享与我 ====================
@DeleteMapping("/delete-received")
@Operation(summary = "删除接收到的共享联系人")
@Parameter(name = "contactId", description = "联系人编号", required = true, example = "1024")
public CommonResult<Boolean> deleteReceivedContact(@RequestParam("contactId") Long contactId) {
contactService.deleteReceivedContact(contactId, getLoginUserId());
return success(true);
}
@PutMapping("/handle-share")
@Operation(summary = "处理外部联系人共享")
public CommonResult<Boolean> handleContactShare(@Valid @RequestBody OaContactHandleReqVO handleReqVO) {
contactService.handleContactShare(handleReqVO.getContactId(), handleReqVO.getCategoryId(), getLoginUserId());
return success(true);
}
// ==================== 公共查询 ====================
@GetMapping("/get")
@Operation(summary = "获得外部联系人详情")
@Parameter(name = "id", description = "联系人编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
public CommonResult<OaContactRespVO> getContact(@RequestParam("id") Long id) {
OaContactDO contact = contactService.getContact(id, getLoginUserId());
return success(buildContactRespVO(contact));
}
@GetMapping("/my-page")
@Operation(summary = "获得我的联系人分页")
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
public CommonResult<PageResult<OaContactRespVO>> getMyContactPage(@Valid OaContactPageReqVO pageReqVO) {
PageResult<OaContactDO> pageResult = contactService.getMyContactPage(pageReqVO, getLoginUserId());
return success(new PageResult<>(buildContactRespVOList(pageResult.getList()), pageResult.getTotal()));
}
@GetMapping("/received-page")
@Operation(summary = "获得共享给我的联系人分页")
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
public CommonResult<PageResult<OaContactRespVO>> getReceivedContactPage(@Valid OaContactPageReqVO pageReqVO) {
PageResult<OaContactDO> pageResult = contactService.getReceivedContactPage(pageReqVO, getLoginUserId());
return success(new PageResult<>(buildContactRespVOList(pageResult.getList()), pageResult.getTotal()));
}
@GetMapping("/shared-page")
@Operation(summary = "获得我共享的联系人分页")
@PreAuthorize("@ss.hasPermission('oa:contact:query')")
public CommonResult<PageResult<OaContactRespVO>> getSharedContactPage(@Valid OaContactPageReqVO pageReqVO) {
PageResult<OaContactShareDO> pageResult = contactService.getSharedContactPage(pageReqVO, getLoginUserId());
List<OaContactDO> contacts = contactService.getContactList(convertSet(pageResult.getList(), OaContactShareDO::getContactId));
// 拼接 VO
Map<Long, OaContactRespVO> contactMap = convertMap(buildContactRespVOList(contacts), OaContactRespVO::getId);
return success(new PageResult<>(convertList(pageResult.getList(), share -> {
OaContactRespVO contact = BeanUtils.toBean(contactMap.get(share.getContactId()), OaContactRespVO.class);
contact.setShare(CollUtil.findOne(contact.getShares(), item -> ObjectUtil.equal(item.getId(), share.getId())));
return contact;
}), pageResult.getTotal()));
}
// ==================== 拼接 VO ====================
/**
* 拼接联系人详情
*
* @param contact 联系人
* @return 联系人响应
*/
private OaContactRespVO buildContactRespVO(OaContactDO contact) {
if (contact == null) {
return null;
}
return CollUtil.getFirst(buildContactRespVOList(Collections.singletonList(contact)));
}
/**
* 拼接外部联系人响应列表
*
* @param contacts 联系人列表
* @return 联系人响应列表
*/
private List<OaContactRespVO> buildContactRespVOList(List<OaContactDO> contacts) {
if (CollUtil.isEmpty(contacts)) {
return Collections.emptyList();
}
// 1.1 批量查询共享关系
Map<Long, List<OaContactShareDO>> contactShareListMap = contactService.getContactShareListMap(
convertSet(contacts, OaContactDO::getId));
List<OaContactShareDO> shares = convertListByFlatMap(contactShareListMap.values(), List::stream);
// 1.2 批量查询创建人和接收人信息
Set<Long> userIds = convertSet(contacts, contact -> NumberUtils.parseLong(contact.getCreator()));
userIds.addAll(convertSet(shares, OaContactShareDO::getUserId));
userIds.addAll(convertSet(shares, share -> NumberUtils.parseLong(share.getCreator())));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
// 1.3 批量查询创建人和接收人的分类
Set<Long> categoryIds = convertSet(contacts, OaContactDO::getCategoryId);
categoryIds.addAll(convertSet(shares, OaContactShareDO::getCategoryId));
Map<Long, OaContactCategoryDO> categoryMap = contactCategoryService.getContactCategoryMap(categoryIds);
Long loginUserId = getLoginUserId();
// 2. 拼接展示字段,接收人仅能查看自己的共享关系
return convertList(contacts, contactDO -> {
OaContactRespVO contact = BeanUtils.toBean(contactDO, OaContactRespVO.class)
.setOwnerUserId(NumberUtils.parseLong(contactDO.getCreator()));
MapUtils.findAndThen(categoryMap, contact.getCategoryId(),
category -> contact.setCategoryName(category.getName()));
MapUtils.findAndThen(userMap, contact.getOwnerUserId(),
user -> contact.setOwnerUserName(user.getNickname()));
List<OaContactShareDO> contactShares = contactShareListMap.getOrDefault(contact.getId(),
Collections.emptyList());
// 仅展示本人发出的共享,不将初始化的本人关联计入共享人数
List<OaContactShareDO> visibleContactShares = filterList(contactShares,
share -> ObjectUtil.equal(NumberUtils.parseLong(share.getCreator()), loginUserId)
&& ObjectUtil.notEqual(share.getUserId(), loginUserId));
contact.setShares(convertList(visibleContactShares, share -> {
OaContactRespVO.Share shareVO = BeanUtils.toBean(share, OaContactRespVO.Share.class);
MapUtils.findAndThen(categoryMap, share.getCategoryId(),
category -> shareVO.setCategoryName(category.getName()));
MapUtils.findAndThen(userMap, NumberUtils.parseLong(share.getCreator()),
user -> shareVO.setCreatorName(user.getNickname()));
MapUtils.findAndThen(userMap, share.getUserId(),
user -> shareVO.setUserName(user.getNickname()).setUserAvatar(user.getAvatar()));
return shareVO;
}));
OaContactShareDO currentUserShare = CollUtil.findOne(contactShares,
share -> share.getUserId().equals(loginUserId));
if (currentUserShare != null) {
MapUtils.findAndThen(userMap, NumberUtils.parseLong(currentUserShare.getCreator()),
user -> contact.setSharerName(user.getNickname()));
contact.setHandleStatus(currentUserShare.getHandleStatus())
.setSharedCategoryId(currentUserShare.getCategoryId());
MapUtils.findAndThen(categoryMap, currentUserShare.getCategoryId(),
category -> contact.setSharedCategoryName(category.getName()));
}
return contact;
});
}
}
@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - OA 联系人共享处理 Request VO")
@Data
public class OaContactHandleReqVO {
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "联系人编号不能为空")
private Long contactId;
@Schema(description = "接收人分类编号", example = "1024")
private Long categoryId;
}
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Pattern;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - OA 外部联系人分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaContactPageReqVO extends PageParam {
@Schema(description = "姓名、拼音、手机或公司名称关键字", example = "张三")
private String keyword;
@Schema(description = "分类编号", example = "1024")
private Long categoryId;
@Schema(description = "是否已处理", example = "false")
private Boolean handleStatus;
@Schema(description = "姓名拼音首字母,空字符串表示全部", example = "A")
@Pattern(regexp = "^[A-Z]?$", message = "姓名拼音首字母必须为 A-Z")
private String alphabet;
}
@@ -0,0 +1,112 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - OA 外部联系人 Response VO")
@Data
public class OaContactRespVO {
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "创建人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long ownerUserId;
@Schema(description = "创建人用户昵称", example = "芋道源码")
private String ownerUserName;
@Schema(description = "分类编号", example = "1024")
private Long categoryId;
@Schema(description = "分类名称", example = "客户")
private String categoryName;
@Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
private String name;
@Schema(description = "姓名拼音", example = "zhangsan")
private String pinyin;
@Schema(description = "性别", example = "1")
private Integer sex;
@Schema(description = "手机号码", example = "13800138000")
private String mobile;
@Schema(description = "邮箱", example = "yudao@example.com")
private String email;
@Schema(description = "地址", example = "上海市浦东新区世纪大道 100 号")
private String address;
@Schema(description = "公司名称", example = "芋道科技有限公司")
private String companyName;
@Schema(description = "公司电话", example = "021-12345678")
private String companyPhone;
@Schema(description = "头像地址", example = "https://example.com/image.png")
private String avatar;
@Schema(description = "备注", example = "请提前联系行政部确认")
private String remark;
@Schema(description = "共享记录列表")
private List<Share> shares;
@Schema(description = "当前行的共享关系,仅我共享的列表返回")
private Share share;
@Schema(description = "分享给当前用户的共享人昵称", example = "芋道源码")
private String sharerName;
@Schema(description = "当前接收人的处理状态", example = "false")
private Boolean handleStatus;
@Schema(description = "当前接收人的分类编号", example = "1024")
private Long sharedCategoryId;
@Schema(description = "当前接收人的分类名称", example = "客户联系人")
private String sharedCategoryName;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime createTime;
@Schema(description = "管理后台 - OA 联系人共享 Response VO")
@Data
public static class Share {
@Schema(description = "共享记录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "共享接收人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long userId;
@Schema(description = "实际共享人昵称", example = "张三")
private String creatorName;
@Schema(description = "共享接收人用户昵称", example = "芋道源码")
private String userName;
@Schema(description = "共享接收人用户头像", example = "https://example.com/avatar.png")
private String userAvatar;
@Schema(description = "分类编号", example = "1024")
private Long categoryId;
@Schema(description = "接收人的分类名称", example = "客户")
private String categoryName;
@Schema(description = "处理状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean handleStatus;
@Schema(description = "共享时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1789347600000")
private LocalDateTime createTime;
}
}
@@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - OA 外部联系人新增/修改 Request VO")
@Data
public class OaContactSaveReqVO {
@Schema(description = "联系人编号", example = "1024")
private Long id;
@Schema(description = "分类编号", example = "1024")
private Long categoryId;
@Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
@NotBlank(message = "姓名不能为空")
@Size(max = 50, message = "姓名长度不能超过 50 个字符")
private String name;
@Schema(description = "性别", example = "1")
private Integer sex;
@Schema(description = "手机号码", requiredMode = Schema.RequiredMode.REQUIRED, example = "13800138000")
@NotBlank(message = "手机号码不能为空")
@Mobile
@Size(max = 20, message = "手机号码长度不能超过 20 个字符")
private String mobile;
@Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED, example = "user@example.com")
@NotBlank(message = "邮箱不能为空")
@Email(message = "邮箱格式不正确")
@Size(max = 100, message = "邮箱长度不能超过 100 个字符")
private String email;
@Schema(description = "地址", example = "上海市浦东新区世纪大道 100 号")
@Size(max = 255, message = "地址长度不能超过 255 个字符")
private String address;
@Schema(description = "公司名称", example = "芋道科技有限公司")
@Size(max = 100, message = "公司名称长度不能超过 100 个字符")
private String companyName;
@Schema(description = "公司电话", example = "021-12345678")
@Size(max = 30, message = "公司电话长度不能超过 30 个字符")
private String companyPhone;
@Schema(description = "头像地址", example = "https://example.com/image.png")
@Size(max = 512, message = "头像地址长度不能超过 512 个字符")
private String avatar;
@Schema(description = "备注", example = "请提前联系行政部确认")
@Size(max = 500, message = "备注长度不能超过 500 个字符")
private String remark;
}
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - OA 联系人共享 Request VO")
@Data
public class OaContactShareReqVO {
@Schema(description = "联系人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "联系人编号不能为空")
private Long contactId;
@Schema(description = "共享接收人用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
@NotEmpty(message = "共享接收人不能为空")
private List<@NotNull(message = "共享接收人编号不能为空") Long> userIds;
}
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - OA 联系人分类 Response VO")
@Data
public class OaContactCategoryRespVO {
@Schema(description = "分类编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "客户")
private String name;
@Schema(description = "显示排序", example = "10")
private Integer sort;
@Schema(description = "创建时间", type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime createTime;
}
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.oa.controller.admin.contact.vo.category;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - OA 联系人分类新增/修改 Request VO")
@Data
public class OaContactCategorySaveReqVO {
@Schema(description = "分类编号", example = "1024")
private Long id;
@Schema(description = "分类名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "客户")
@NotBlank(message = "分类名称不能为空")
@Size(max = 50, message = "分类名称长度不能超过 50 个字符")
private String name;
@Schema(description = "显示排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "显示排序不能为空")
private Integer sort;
}
@@ -0,0 +1,223 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion.OaDiscussionPageReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion.OaDiscussionRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion.OaDiscussionSaveReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote.OaVoteOptionRespVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionDO;
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionLikeDO;
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionReplyDO;
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaVoteOptionDO;
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaVoteRecordDO;
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionService;
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionReplyService;
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionLikeService;
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionVoteService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
@Tag(name = "管理后台 - OA 讨论")
@RestController
@RequestMapping("/oa/discussion")
@Validated
public class OaDiscussionController {
@Resource
private OaDiscussionService discussionService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private OaDiscussionLikeService discussionLikeService;
@Resource
private OaDiscussionReplyService discussionReplyService;
@Resource
private OaDiscussionVoteService discussionVoteService;
@PostMapping("/create")
@Operation(summary = "创建讨论")
@PreAuthorize("@ss.hasPermission('oa:discussion:create')")
public CommonResult<Long> createDiscussion(@Valid @RequestBody OaDiscussionSaveReqVO createReqVO) {
return success(discussionService.createDiscussion(createReqVO, getLoginUserId()));
}
@PutMapping("/update")
@Operation(summary = "更新讨论")
@PreAuthorize("@ss.hasPermission('oa:discussion:update')")
public CommonResult<Boolean> updateDiscussion(@Valid @RequestBody OaDiscussionSaveReqVO updateReqVO) {
discussionService.updateDiscussion(updateReqVO, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除讨论")
@Parameter(name = "id", description = "讨论编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:discussion:delete')")
public CommonResult<Boolean> deleteDiscussion(@RequestParam("id") Long id) {
discussionService.deleteDiscussion(id, getLoginUserId());
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得讨论详情")
@Parameter(name = "visit", description = "是否记录本次访问,默认不记录", example = "true")
@Parameter(name = "id", description = "讨论编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:discussion:query')")
public CommonResult<OaDiscussionRespVO> getDiscussion(@RequestParam("id") Long id,
@RequestParam(value = "visit", defaultValue = "false") Boolean visit) {
// 1.1 查询讨论
OaDiscussionDO discussion = discussionService.validateDiscussionExists(id);
// 1.2 按需记录本次访问
if (Boolean.TRUE.equals(visit)) {
discussionService.increaseDiscussionVisitCount(id);
discussion.setVisitCount(discussion.getVisitCount() + 1);
}
// 2. 拼接讨论详情
return success(buildDiscussionRespVO(discussion));
}
@GetMapping("/page")
@Operation(summary = "获得讨论分页")
@PreAuthorize("@ss.hasPermission('oa:discussion:query')")
public CommonResult<PageResult<OaDiscussionRespVO>> getDiscussionPage(
@Valid OaDiscussionPageReqVO pageReqVO) {
PageResult<OaDiscussionDO> pageResult = discussionService.getDiscussionPage(pageReqVO);
return success(new PageResult<>(buildDiscussionRespVOList(pageResult.getList()), pageResult.getTotal()));
}
@GetMapping("/manage-page")
@Operation(summary = "获得管理范围内的讨论分页")
@PreAuthorize("@ss.hasPermission('oa:discussion:query')")
public CommonResult<PageResult<OaDiscussionRespVO>> getDiscussionManagePage(
@Valid OaDiscussionPageReqVO pageReqVO) {
PageResult<OaDiscussionDO> pageResult = discussionService.getDiscussionManagePage(pageReqVO, getLoginUserId());
return success(new PageResult<>(buildDiscussionRespVOList(pageResult.getList()), pageResult.getTotal()));
}
// ==================== 拼接 VO ====================
/**
* 拼接讨论详情
*
* @param discussion 讨论
* @return 讨论响应
*/
private OaDiscussionRespVO buildDiscussionRespVO(OaDiscussionDO discussion) {
if (discussion == null) {
return null;
}
return CollUtil.getFirst(buildDiscussionRespVOList(Collections.singletonList(discussion)));
}
/**
* 拼接讨论响应列表
*
* @param discussions 讨论列表
* @return 讨论响应列表
*/
private List<OaDiscussionRespVO> buildDiscussionRespVOList(List<OaDiscussionDO> discussions) {
if (CollUtil.isEmpty(discussions)) {
return Collections.emptyList();
}
// 1.1 批量查询回复和点赞
Set<Long> discussionIds = convertSet(discussions, OaDiscussionDO::getId);
Map<Long, List<OaDiscussionReplyDO>> replyListMap = convertMultiMap(
discussionReplyService.getDiscussionReplyList(discussionIds), OaDiscussionReplyDO::getDiscussionId);
List<OaDiscussionLikeDO> discussionLikes = discussionLikeService.getDiscussionLikeList(discussionIds);
Map<Long, List<OaDiscussionLikeDO>> likeListMap = convertMultiMap(discussionLikes,
OaDiscussionLikeDO::getDiscussionId);
// 1.2 批量查询投票选项和记录
Map<Long, List<OaVoteOptionDO>> voteOptionListMap = convertMultiMap(
discussionVoteService.getVoteOptionList(discussionIds), OaVoteOptionDO::getDiscussionId);
List<OaVoteRecordDO> voteRecords = discussionVoteService.getVoteRecordList(discussionIds);
Map<Long, List<OaVoteRecordDO>> voteRecordListMap = convertMultiMap(voteRecords,
OaVoteRecordDO::getDiscussionId);
// 1.3 批量查询发布人、点赞人和投票人
Set<Long> userIds = convertSet(discussions, OaDiscussionDO::getUserId);
userIds.addAll(convertSet(discussionLikes, OaDiscussionLikeDO::getUserId));
userIds.addAll(convertSet(voteRecords, OaVoteRecordDO::getUserId));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
Long loginUserId = getLoginUserId();
// 2. 转换并拼接讨论展示字段
return BeanUtils.toBean(discussions, OaDiscussionRespVO.class, discussion -> {
findAndThen(userMap, discussion.getUserId(), user -> discussion.setUserName(user.getNickname()));
discussion.setReplyCount(CollUtil.size(replyListMap.get(discussion.getId())));
List<OaDiscussionLikeDO> likes = likeListMap.getOrDefault(discussion.getId(), Collections.emptyList());
discussion.setLikeCount(likes.size());
discussion.setLiked(CollUtil.findOne(likes, like -> like.getUserId().equals(loginUserId)) != null);
discussion.setLikeUserNames(convertList(likes,
like -> getUserNickname(userMap, like.getUserId())));
discussion.setVoteOptions(buildVoteOptionRespVOList(
voteOptionListMap.getOrDefault(discussion.getId(), Collections.emptyList()),
voteRecordListMap.getOrDefault(discussion.getId(), Collections.emptyList()), userMap, loginUserId));
});
}
/**
* 拼接投票选项响应列表
*
* @param voteOptions 投票选项列表
* @param voteRecords 投票记录列表
* @param userMap 用户 Map
* @param loginUserId 当前登录用户编号
* @return 投票选项响应列表
*/
private List<OaVoteOptionRespVO> buildVoteOptionRespVOList(List<OaVoteOptionDO> voteOptions,
List<OaVoteRecordDO> voteRecords,
Map<Long, AdminUserRespDTO> userMap,
Long loginUserId) {
Map<Long, List<OaVoteRecordDO>> optionVoteRecordListMap = convertMultiMap(voteRecords,
OaVoteRecordDO::getOptionId);
return BeanUtils.toBean(voteOptions, OaVoteOptionRespVO.class, voteOption -> {
voteOption.setVoteCount(0).setVoted(false).setVoterUserNames(Collections.emptyList());
findAndThen(optionVoteRecordListMap, voteOption.getId(), records ->
voteOption.setVoteCount(records.size())
.setVoted(CollUtil.findOne(records, record -> record.getUserId().equals(loginUserId)) != null)
.setVoterUserNames(convertList(records, record -> getUserNickname(userMap, record.getUserId()))));
});
}
/**
* 获得用户昵称
*
* @param userMap 用户 Map
* @param userId 用户编号
* @return 用户昵称
*/
private static String getUserNickname(Map<Long, AdminUserRespDTO> userMap, Long userId) {
AdminUserRespDTO user = userMap.get(userId);
return user != null ? user.getNickname() : null;
}
}
@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.like.OaDiscussionLikeReqVO;
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionLikeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - OA 讨论点赞")
@RestController
@RequestMapping("/oa/discussion-like")
@Validated
public class OaDiscussionLikeController {
@Resource
private OaDiscussionLikeService discussionLikeService;
@PostMapping("/create")
@Operation(summary = "点赞讨论或主回复")
public CommonResult<Boolean> createDiscussionLike(@Valid @RequestBody OaDiscussionLikeReqVO reqVO) {
discussionLikeService.createDiscussionLike(reqVO.getDiscussionId(), reqVO.getReplyId(), getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "取消点赞讨论或主回复")
public CommonResult<Boolean> deleteDiscussionLike(@Valid OaDiscussionLikeReqVO reqVO) {
discussionLikeService.deleteDiscussionLike(reqVO.getDiscussionId(), reqVO.getReplyId(), getLoginUserId());
return success(true);
}
}
@@ -0,0 +1,133 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply.OaDiscussionReplyPageReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply.OaDiscussionReplyRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply.OaDiscussionReplyCreateReqVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionLikeDO;
import cn.iocoder.yudao.module.oa.dal.dataobject.discussion.OaDiscussionReplyDO;
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionReplyService;
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionLikeService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Stream;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
import static cn.iocoder.yudao.framework.common.util.collection.MapUtils.findAndThen;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - OA 讨论回复")
@RestController
@RequestMapping("/oa/discussion-reply")
@Validated
public class OaDiscussionReplyController {
@Resource
private OaDiscussionReplyService discussionReplyService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private OaDiscussionLikeService discussionLikeService;
@PostMapping("/create")
@Operation(summary = "创建讨论回复")
public CommonResult<Long> createDiscussionReply(
@Valid @RequestBody OaDiscussionReplyCreateReqVO createReqVO) {
return success(discussionReplyService.createDiscussionReply(createReqVO, getLoginUserId()));
}
@DeleteMapping("/delete")
@Operation(summary = "删除讨论回复")
@Parameter(name = "id", description = "回复编号", required = true, example = "1024")
public CommonResult<Boolean> deleteDiscussionReply(@RequestParam("id") Long id) {
discussionReplyService.deleteDiscussionReply(id, getLoginUserId());
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获得讨论回复分页")
@PreAuthorize("@ss.hasPermission('oa:discussion:query')")
public CommonResult<PageResult<OaDiscussionReplyRespVO>> getDiscussionReplyPage(
@Valid OaDiscussionReplyPageReqVO pageReqVO) {
PageResult<OaDiscussionReplyDO> pageResult = discussionReplyService.getDiscussionReplyPage(pageReqVO);
// 1. 批量查询当前页楼层内的子回复
List<OaDiscussionReplyDO> replies = new ArrayList<>(pageResult.getList());
replies.addAll(discussionReplyService.getDiscussionReplyDescendantList(pageResult.getList()));
// 2. 批量拼接展示字段,再按照父回复关系组织楼层
List<OaDiscussionReplyRespVO> replyVOs = buildDiscussionReplyRespVOList(replies);
Map<Long, OaDiscussionReplyRespVO> replyMap = convertMap(replyVOs, OaDiscussionReplyRespVO::getId);
replyVOs.forEach(reply -> reply.setChildren(new ArrayList<>()));
replyVOs.forEach(reply -> findAndThen(replyMap, reply.getParentId(),
parent -> parent.getChildren().add(reply)));
return success(new PageResult<>(convertList(pageResult.getList(),
reply -> replyMap.get(reply.getId())), pageResult.getTotal()));
}
// ==================== 拼接 VO ====================
/**
* 拼接讨论回复响应列表
*
* @param replies 讨论回复列表
* @return 讨论回复响应列表
*/
private List<OaDiscussionReplyRespVO> buildDiscussionReplyRespVOList(List<OaDiscussionReplyDO> replies) {
if (CollUtil.isEmpty(replies)) {
return Collections.emptyList();
}
// 1.1 批量查询回复点赞
Map<Long, List<OaDiscussionLikeDO>> replyLikeListMap = discussionLikeService.getReplyLikeListMap(
convertSet(replies, OaDiscussionReplyDO::getId,
reply -> ObjectUtil.equal(reply.getParentId(), OaDiscussionReplyDO.PARENT_ID_ROOT)));
// 1.2 批量查询回复人、被回复人和点赞人
Set<Long> userIds = convertSetByFlatMap(replies,
reply -> Stream.of(reply.getUserId(), reply.getReplyUserId()));
userIds.addAll(convertSetByFlatMap(replyLikeListMap.values(),
likes -> likes.stream().map(OaDiscussionLikeDO::getUserId)));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
Long loginUserId = getLoginUserId();
// 2. 转换并拼接回复展示字段
return BeanUtils.toBean(replies, OaDiscussionReplyRespVO.class, reply -> {
findAndThen(userMap, reply.getUserId(), user -> reply.setUserName(user.getNickname()));
findAndThen(userMap, reply.getReplyUserId(), user -> reply.setReplyUserName(user.getNickname()));
List<OaDiscussionLikeDO> likes = replyLikeListMap.getOrDefault(reply.getId(), Collections.emptyList());
reply.setLikeCount(likes.size())
.setLiked(CollUtil.findOne(likes, like -> like.getUserId().equals(loginUserId)) != null)
.setLikeUserNames(convertList(likes, like -> {
AdminUserRespDTO user = userMap.get(like.getUserId());
return user != null ? user.getNickname() : null;
}));
});
}
}
@@ -0,0 +1,35 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote.OaDiscussionVoteReqVO;
import cn.iocoder.yudao.module.oa.service.discussion.OaDiscussionVoteService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - OA 讨论投票")
@RestController
@RequestMapping("/oa/discussion-vote")
@Validated
public class OaDiscussionVoteController {
@Resource
private OaDiscussionVoteService discussionVoteService;
@PostMapping("/create")
@Operation(summary = "参与讨论投票")
public CommonResult<Boolean> voteDiscussion(@Valid @RequestBody OaDiscussionVoteReqVO voteReqVO) {
discussionVoteService.voteDiscussion(voteReqVO.getDiscussionId(), voteReqVO.getOptionIds(), getLoginUserId());
return success(true);
}
}
@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion;
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.discussion.OaDiscussionTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - OA 讨论分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaDiscussionPageReqVO extends SortablePageParam {
@Schema(description = "标题", example = "产品方案讨论")
private String title;
@Schema(description = "讨论类型", example = "1")
@InEnum(value = OaDiscussionTypeEnum.class, message = "讨论类型必须是 {value}")
private Integer type;
@Schema(description = "发布人用户编号", example = "1")
private Long userId;
@Schema(description = "创建时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
@@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote.OaVoteOptionRespVO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - OA 讨论 Response VO")
@Data
public class OaDiscussionRespVO {
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "发布人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@Schema(description = "发布人用户昵称", example = "芋道源码")
private String userName;
@Schema(description = "讨论类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer type;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "下周团队会议时间征集")
private String title;
@Schema(description = "内容", example = "请大家反馈下周团队会议的时间安排。")
private String content;
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
private List<String> fileUrls;
@Schema(description = "访问次数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer visitCount;
@Schema(description = "回复数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
private Integer replyCount;
@Schema(description = "点赞数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3")
private Integer likeCount;
@Schema(description = "当前用户是否已点赞", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean liked;
@Schema(description = "点赞人用户昵称列表", example = "[\"张三\", \"李四\"]")
private List<String> likeUserNames;
@Schema(description = "投票是否允许多选", example = "false")
private Boolean voteMultiple;
@Schema(description = "投票开始时间", type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime voteStartTime;
@Schema(description = "投票结束时间", type = "integer", format = "int64", example = "1789380000000")
private LocalDateTime voteEndTime;
@Schema(description = "投票选项列表")
private List<OaVoteOptionRespVO> voteOptions;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime createTime;
}
@@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.discussion;
import cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote.OaVoteOptionSaveReqVO;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.discussion.OaDiscussionTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - OA 讨论新增/修改 Request VO")
@Data
public class OaDiscussionSaveReqVO {
@Schema(description = "讨论编号", example = "1024")
private Long id;
@Schema(description = "讨论类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "讨论类型不能为空")
@InEnum(value = OaDiscussionTypeEnum.class, message = "讨论类型必须是 {value}")
private Integer type;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "下周团队会议时间征集")
@NotBlank(message = "标题不能为空")
@Size(max = 255, message = "标题长度不能超过 255 个字符")
private String title;
@Schema(description = "内容", example = "请大家反馈下周团队会议的时间安排。")
private String content;
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
private List<@NotBlank(message = "附件地址不能为空") @Size(max = 512, message = "附件地址长度不能超过 512 个字符") String> fileUrls;
@Schema(description = "投票是否允许多选", example = "false")
private Boolean voteMultiple;
@Schema(description = "投票开始时间", type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime voteStartTime;
@Schema(description = "投票结束时间", type = "integer", format = "int64", example = "1789380000000")
private LocalDateTime voteEndTime;
@Schema(description = "投票选项列表")
@Valid
private List<@NotNull(message = "投票选项不能为空") OaVoteOptionSaveReqVO> voteOptions;
@JsonIgnore
@AssertTrue(message = "投票至少需要两个选项,并设置正确的开始和结束时间")
@Schema(hidden = true)
public boolean isVoteConfigValid() {
if (ObjectUtil.notEqual(OaDiscussionTypeEnum.VOTE.getType(), type)) {
return true;
}
if (voteMultiple == null || voteStartTime == null || voteEndTime == null) {
return false;
}
return voteEndTime.isAfter(voteStartTime) && CollUtil.size(voteOptions) >= 2;
}
}
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.like;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import lombok.Data;
@Schema(description = "管理后台 - OA 讨论点赞 Request VO")
@Data
public class OaDiscussionLikeReqVO {
@Schema(description = "讨论编号,与回复编号必须且只能填写一个", example = "1024")
private Long discussionId;
@Schema(description = "回复编号,与讨论编号必须且只能填写一个", example = "2048")
private Long replyId;
@JsonIgnore
@AssertTrue(message = "讨论编号和回复编号必须且只能填写一个")
@Schema(hidden = true)
public boolean isTargetValid() {
if (discussionId != null) {
return replyId == null;
}
return replyId != null;
}
}
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.AssertTrue;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
@Schema(description = "管理后台 - OA 讨论回复新增 Request VO")
@Data
public class OaDiscussionReplyCreateReqVO {
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "讨论编号不能为空")
private Long discussionId;
@Schema(description = "父回复编号", example = "0")
private Long parentId;
@Schema(description = "回复内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "已收到,将按时完成。")
@NotBlank(message = "回复内容不能为空")
private String content;
/**
* 楼内评论对齐源端 255 字限制,根回复使用 TEXT 正文。
*
* @return 评论长度是否合法
*/
@AssertTrue(message = "楼内评论不能超过 255 个字符")
@JsonIgnore
public boolean isCommentLengthValid() {
return parentId == null || parentId == 0L || content == null || content.length() <= 255;
}
}
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply;
import cn.iocoder.yudao.framework.common.pojo.SortablePageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - OA 讨论回复分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaDiscussionReplyPageReqVO extends SortablePageParam {
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "讨论编号不能为空")
private Long discussionId;
@Schema(description = "回复人用户编号", example = "1")
private Long userId;
}
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.reply;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - OA 讨论回复 Response VO")
@Data
public class OaDiscussionReplyRespVO {
@Schema(description = "回复编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
private Long discussionId;
@Schema(description = "回复人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long userId;
@Schema(description = "回复人用户昵称", example = "芋道源码")
private String userName;
@Schema(description = "父回复编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Long parentId;
@Schema(description = "被回复人用户编号", example = "2")
private Long replyUserId;
@Schema(description = "被回复人用户昵称", example = "管理员")
private String replyUserName;
@Schema(description = "回复内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "已收到,将按时完成。")
private String content;
@Schema(description = "点赞数", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer likeCount;
@Schema(description = "当前用户是否已点赞", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean liked;
@Schema(description = "点赞人用户昵称列表", example = "[\"张三\", \"李四\"]")
private List<String> likeUserNames;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime createTime;
@Schema(description = "楼层内的子回复")
private List<OaDiscussionReplyRespVO> children;
}
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - OA 讨论投票 Request VO")
@Data
public class OaDiscussionVoteReqVO {
@Schema(description = "讨论编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "讨论编号不能为空")
private Long discussionId;
@Schema(description = "投票选项编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2]")
@NotEmpty(message = "投票选项不能为空")
private List<@NotNull(message = "投票选项编号不能为空") Long> optionIds;
}
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - OA 投票选项 Response VO")
@Data
public class OaVoteOptionRespVO {
@Schema(description = "投票选项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "选项标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "周五下午")
private String title;
@Schema(description = "选项颜色", example = "#409EFF")
private String color;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
private Integer sort;
@Schema(description = "投票数", requiredMode = Schema.RequiredMode.REQUIRED, example = "5")
private Integer voteCount;
@Schema(description = "当前用户是否已选择", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean voted;
@Schema(description = "投票人用户昵称列表", example = "[\"张三\", \"李四\"]")
private List<String> voterUserNames;
}
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.oa.controller.admin.discussion.vo.vote;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - OA 投票选项新增/修改 Request VO")
@Data
public class OaVoteOptionSaveReqVO {
@Schema(description = "选项标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "周五下午")
@NotBlank(message = "投票选项标题不能为空")
@Size(max = 200, message = "投票选项标题长度不能超过 200 个字符")
private String title;
@Schema(description = "选项颜色", example = "#409EFF")
@Size(max = 20, message = "投票选项颜色长度不能超过 20 个字符")
private String color;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
@NotNull(message = "显示顺序不能为空")
private Integer sort;
}
@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.oa.controller.admin.file;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.oa.service.file.OaFileFavoriteService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 云盘收藏")
@RestController
@RequestMapping("/oa/file-favorite")
@Validated
public class OaFileFavoriteController {
@Resource
private OaFileFavoriteService fileFavoriteService;
@PostMapping("/create")
@Operation(summary = "收藏文件")
@Parameter(name = "nodeId", description = "节点编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:file:query')")
public CommonResult<Boolean> createFileFavorite(@RequestParam("nodeId") Long nodeId) {
fileFavoriteService.createFileFavorite(nodeId, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "取消收藏文件")
@Parameter(name = "nodeId", description = "节点编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:file:query')")
public CommonResult<Boolean> deleteFileFavorite(@RequestParam("nodeId") Long nodeId) {
fileFavoriteService.deleteFileFavorite(nodeId, getLoginUserId());
return success(true);
}
}
@@ -0,0 +1,116 @@
package cn.iocoder.yudao.module.oa.controller.admin.file;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.oa.controller.admin.file.vo.node.*;
import cn.iocoder.yudao.module.oa.service.file.OaFileNodeService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 云盘文件")
@RestController
@RequestMapping("/oa/file-node")
@Validated
public class OaFileNodeController {
@Resource
private OaFileNodeService fileNodeService;
@GetMapping("/get-storage")
@Operation(summary = "获得本人云盘空间")
@PreAuthorize("@ss.hasPermission('oa:file:query')")
public CommonResult<OaFileStorageRespVO> getFileStorage() {
return success(fileNodeService.getFileStorage(getLoginUserId()));
}
@GetMapping("/page")
@Operation(summary = "获得云盘文件分页")
@PreAuthorize("@ss.hasPermission('oa:file:query')")
public CommonResult<PageResult<OaFileNodeRespVO>> getFileNodePage(@Valid OaFileNodePageReqVO pageReqVO) {
return success(fileNodeService.getFileNodePage(pageReqVO, getLoginUserId()));
}
@GetMapping("/directory-list")
@Operation(summary = "获得本人可用目录列表")
@PreAuthorize("@ss.hasPermission('oa:file:query')")
public CommonResult<List<OaFileNodeRespVO>> getFileDirectoryList() {
return success(fileNodeService.getFileDirectoryList(getLoginUserId()));
}
@PostMapping("/create")
@Operation(summary = "创建云盘文件或目录")
@PreAuthorize("@ss.hasPermission('oa:file:create')")
public CommonResult<Long> createFileNode(@Valid @RequestBody OaFileNodeSaveReqVO reqVO) {
return success(fileNodeService.createFileNode(reqVO, getLoginUserId()));
}
@PutMapping("/update-name")
@Operation(summary = "重命名云盘文件")
@PreAuthorize("@ss.hasPermission('oa:file:update')")
public CommonResult<Boolean> updateFileNodeName(@Valid @RequestBody OaFileNodeRenameReqVO reqVO) {
fileNodeService.updateFileNodeName(reqVO, getLoginUserId());
return success(true);
}
@PutMapping("/update-parent")
@Operation(summary = "移动云盘文件")
@PreAuthorize("@ss.hasPermission('oa:file:update')")
public CommonResult<Boolean> updateFileNodeParent(@Valid @RequestBody OaFileNodeMoveReqVO reqVO) {
fileNodeService.updateFileNodeParent(reqVO, getLoginUserId());
return success(true);
}
@PostMapping("/copy")
@Operation(summary = "复制云盘文件或目录")
@PreAuthorize("@ss.hasPermission('oa:file:create')")
public CommonResult<Long> copyFileNode(@Valid @RequestBody OaFileNodeCopyReqVO reqVO) {
return success(fileNodeService.copyFileNode(reqVO, getLoginUserId()));
}
@PutMapping("/recycle")
@Operation(summary = "移入回收站")
@Parameter(name = "id", description = "节点编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:file:delete')")
public CommonResult<Boolean> recycleFileNode(@RequestParam("id") Long id) {
fileNodeService.recycleFileNode(id, getLoginUserId());
return success(true);
}
@PutMapping("/restore")
@Operation(summary = "恢复回收站文件")
@Parameter(name = "id", description = "节点编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:file:delete')")
public CommonResult<Boolean> restoreFileNode(@RequestParam("id") Long id) {
fileNodeService.restoreFileNode(id, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "彻底删除回收站文件")
@Parameter(name = "id", description = "节点编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:file:delete')")
public CommonResult<Boolean> deleteFileNode(@RequestParam("id") Long id) {
fileNodeService.deleteFileNode(id, getLoginUserId());
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得文件详情")
@Parameter(name = "id", description = "节点编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:file:query')")
public CommonResult<OaFileNodeRespVO> getFileNode(@RequestParam("id") Long id) {
return success(fileNodeService.getFileNode(id, getLoginUserId()));
}
}
@@ -0,0 +1,57 @@
package cn.iocoder.yudao.module.oa.controller.admin.file;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.file.vo.permission.OaFilePermissionRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.file.vo.permission.OaFilePermissionSaveReqVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.file.OaFilePermissionDO;
import cn.iocoder.yudao.module.oa.service.file.OaFilePermissionService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 云盘共享权限")
@RestController
@RequestMapping("/oa/file-permission")
@Validated
public class OaFilePermissionController {
@Resource
private OaFilePermissionService filePermissionService;
@GetMapping("/list")
@Operation(summary = "获得文件共享权限")
@Parameter(name = "nodeId", description = "节点编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:file:share')")
public CommonResult<List<OaFilePermissionRespVO>> getFilePermissionList(@RequestParam("nodeId") Long nodeId) {
List<OaFilePermissionDO> permissions = filePermissionService.getFilePermissionList(nodeId, getLoginUserId());
return success(BeanUtils.toBean(permissions, OaFilePermissionRespVO.class));
}
@PostMapping("/save")
@Operation(summary = "保存文件共享权限")
@PreAuthorize("@ss.hasPermission('oa:file:share')")
public CommonResult<Long> saveFilePermission(@Valid @RequestBody OaFilePermissionSaveReqVO reqVO) {
return success(filePermissionService.saveFilePermission(reqVO, getLoginUserId()));
}
@DeleteMapping("/delete")
@Operation(summary = "取消文件共享权限")
@Parameter(name = "id", description = "权限编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:file:share')")
public CommonResult<Boolean> deleteFilePermission(@RequestParam("id") Long id) {
filePermissionService.deleteFilePermission(id, getLoginUserId());
return success(true);
}
}
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 云盘复制 Request VO")
@Data
public class OaFileNodeCopyReqVO {
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "节点编号不能为空")
private Long id;
@Schema(description = "目标目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "目标目录编号不能为空")
private Long parentId;
}
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - 云盘移动 Request VO")
@Data
public class OaFileNodeMoveReqVO {
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "节点编号不能为空")
private Long id;
@Schema(description = "目标目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "目标目录编号不能为空")
private Long parentId;
}
@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.file.OaFileCategoryEnum;
import cn.iocoder.yudao.module.oa.enums.file.OaFileScopeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 云盘文件分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaFileNodePageReqVO extends PageParam {
@Schema(description = "列表范围:my、shared、favorite、recycle", requiredMode = Schema.RequiredMode.REQUIRED, example = "my")
@NotBlank(message = "列表范围不能为空")
@InEnum(value = OaFileScopeEnum.class, message = "列表范围必须是 {value}")
private String scope;
@Schema(description = "父目录编号,不传时查询范围内全部文件", example = "1024")
private Long parentId;
@Schema(description = "名称", example = "办公用品盘点结果.pdf")
@Size(max = 255, message = "名称长度不能超过 255 个字符")
private String name;
@Schema(description = "文件分类", example = "1")
@InEnum(value = OaFileCategoryEnum.class, message = "文件分类必须是 {value}")
private Integer category;
@Schema(description = "创建时间范围", example = "[\"2026-09-14 00:00:00\", \"2026-09-14 23:59:59\"]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
@Schema(description = "管理后台 - 云盘重命名 Request VO")
@Data
public class OaFileNodeRenameReqVO {
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "节点编号不能为空")
private Long id;
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公用品盘点结果.pdf")
@NotBlank(message = "名称不能为空")
@Size(max = 255, message = "名称长度不能超过 255 个字符")
private String name;
}
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 云盘文件 Response VO")
@Data
public class OaFileNodeRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "父目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long parentId;
@Schema(description = "节点类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer type;
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公用品盘点结果.pdf")
private String name;
@Schema(description = "临时文件地址,仅详情中具有下载权限时返回", example = "https://example.com/file.pdf")
private String url;
@Schema(description = "扩展名", example = "pdf")
private String extension;
@Schema(description = "文件分类", example = "1")
private Integer category;
@Schema(description = "大小,单位字节", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long size;
@Schema(description = "节点状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status;
@Schema(description = "创建人", example = "1024")
private String creator;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime createTime;
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime updateTime;
@Schema(description = "当前用户权限级别", example = "1")
private Integer level;
@Schema(description = "是否收藏", example = "false")
private Boolean favorite;
}
@@ -0,0 +1,49 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.file.OaFileNodeTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
@Schema(description = "管理后台 - 云盘文件新增 Request VO")
@Data
public class OaFileNodeSaveReqVO {
@Schema(description = "父目录编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "父目录编号不能为空")
private Long parentId;
@Schema(description = "节点类型:0 文件夹、1 文件", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "节点类型不能为空")
@InEnum(value = OaFileNodeTypeEnum.class, message = "节点类型必须是 {value}")
private Integer type;
@Schema(description = "节点名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公用品盘点结果.pdf")
@NotBlank(message = "节点名称不能为空")
@Size(max = 255, message = "节点名称长度不能超过 255 个字符")
private String name;
@Schema(description = "上传文件地址,文件夹不传", example = "https://example.com/file.pdf")
@Size(max = 2048, message = "文件地址长度不能超过 2048 个字符")
private String url;
@Schema(description = "文件大小,单位字节,文件夹不传", example = "1024")
private Long size;
@AssertTrue(message = "文件地址不能为空")
@JsonIgnore
public boolean isUrlValid() {
return ObjUtil.notEqual(type, OaFileNodeTypeEnum.FILE.getType()) || StrUtil.isNotBlank(url);
}
@AssertTrue(message = "文件大小不能为空且不能小于 0")
@JsonIgnore
public boolean isSizeValid() {
return ObjUtil.notEqual(type, OaFileNodeTypeEnum.FILE.getType()) || (size != null && size >= 0);
}
}
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.node;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 云盘空间 Response VO")
@Data
public class OaFileStorageRespVO {
@Schema(description = "已用容量,单位字节", example = "1024")
private Long usedSize;
@Schema(description = "总容量,单位字节", example = "1073741824")
private Long totalSize;
@Schema(description = "本人正常文件数量,不含目录和回收站", example = "1")
private Long fileCount;
@Schema(description = "本人有效共享节点数量,按文件或目录去重", example = "1")
private Long sharedCount;
@Schema(description = "收到的共享入口数量,不重复统计子节点", example = "1")
private Long receivedCount;
}
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.permission;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 云盘共享权限 Response VO")
@Data
public class OaFilePermissionRespVO {
@Schema(description = "权限编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long nodeId;
@Schema(description = "主体类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer subjectType;
@Schema(description = "主体编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long subjectId;
@Schema(description = "权限级别", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Integer level;
@Schema(description = "是否继承", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean inherit;
@Schema(description = "到期时间", type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime expireTime;
}
@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.oa.controller.admin.file.vo.permission;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.file.OaFilePermissionLevelEnum;
import cn.iocoder.yudao.module.oa.enums.file.OaFileSubjectTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - 云盘共享权限 Request VO")
@Data
public class OaFilePermissionSaveReqVO {
@Schema(description = "节点编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "节点编号不能为空")
private Long nodeId;
@Schema(description = "共享主体", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "共享主体类型不能为空")
@InEnum(value = OaFileSubjectTypeEnum.class, message = "共享主体类型必须是 {value}")
private Integer subjectType;
@Schema(description = "主体编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "主体编号不能为空")
private Long subjectId;
@Schema(description = "权限", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "权限级别不能为空")
@InEnum(value = OaFilePermissionLevelEnum.class, message = "权限级别必须是 {value}")
private Integer level;
@Schema(description = "是否继承到子目录及文件", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否继承不能为空")
private Boolean inherit;
@Schema(description = "到期时间", type = "integer", format = "int64", example = "1789347600000")
@Future(message = "到期时间必须晚于当前时间")
private LocalDateTime expireTime;
}
@@ -0,0 +1,122 @@
package cn.iocoder.yudao.module.oa.controller.admin.leave;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.leave.vo.OaLeaveApplySaveReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.leave.vo.OaLeaveApplySubmitReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.leave.vo.OaLeaveApplyPageReqVO;
import cn.iocoder.yudao.module.oa.controller.admin.leave.vo.OaLeaveApplyRespVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.leave.OaLeaveApplyDO;
import cn.iocoder.yudao.module.oa.service.leave.OaLeaveApplyService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 请假申请")
@RestController
@RequestMapping("/oa/leave-apply")
@Validated
public class OaLeaveApplyController {
@Resource
private OaLeaveApplyService leaveApplyService;
@Resource
private AdminUserApi adminUserApi;
@PostMapping("/create")
@Operation(summary = "创建请假申请草稿")
@PreAuthorize("@ss.hasPermission('oa:leave-apply:create')")
public CommonResult<Long> createLeaveApply(@Valid @RequestBody OaLeaveApplySaveReqVO createReqVO) {
return success(leaveApplyService.createLeaveApply(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新请假申请草稿")
@PreAuthorize("@ss.hasPermission('oa:leave-apply:create')")
public CommonResult<Boolean> updateLeaveApply(@Valid @RequestBody OaLeaveApplySaveReqVO saveReqVO) {
leaveApplyService.updateLeaveApply(saveReqVO, getLoginUserId());
return success(true);
}
@PostMapping("/submit")
@Operation(summary = "提交请假申请")
@PreAuthorize("@ss.hasPermission('oa:leave-apply:create')")
public CommonResult<Boolean> submitLeaveApply(@Valid @RequestBody OaLeaveApplySubmitReqVO submitReqVO) {
leaveApplyService.submitLeaveApply(submitReqVO, getLoginUserId());
return success(true);
}
@GetMapping("/page")
@Operation(summary = "获得本人请假申请分页")
@PreAuthorize("@ss.hasPermission('oa:leave-apply:query')")
public CommonResult<PageResult<OaLeaveApplyRespVO>> getLeaveApplyPage(@Valid OaLeaveApplyPageReqVO pageReqVO) {
PageResult<OaLeaveApplyDO> page = leaveApplyService.getLeaveApplyPage(getLoginUserId(), pageReqVO);
return success(new PageResult<>(buildLeaveApplyRespVOList(page.getList()), page.getTotal()));
}
@GetMapping("/get")
@Operation(summary = "获得请假申请详情")
@Parameter(name = "id", description = "申请编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:leave-apply:query')")
public CommonResult<OaLeaveApplyRespVO> getLeaveApply(@RequestParam("id") Long id) {
OaLeaveApplyDO application = leaveApplyService.getLeaveApply(id);
return success(buildLeaveApplyRespVO(application));
}
// ==================== 拼接 VO ====================
/**
* 拼接请假申请详情
*
* @param application 申请
* @return 请假申请响应
*/
private OaLeaveApplyRespVO buildLeaveApplyRespVO(OaLeaveApplyDO application) {
if (application == null) {
return null;
}
return CollUtil.getFirst(buildLeaveApplyRespVOList(Collections.singletonList(application)));
}
/**
* 批量拼接申请人展示信息
*
* @param applications 申请列表
* @return 响应列表
*/
private List<OaLeaveApplyRespVO> buildLeaveApplyRespVOList(List<OaLeaveApplyDO> applications) {
if (CollUtil.isEmpty(applications)) {
return Collections.emptyList();
}
// 1. 批量查询申请人
Map<Long, AdminUserRespDTO> users = adminUserApi.getUserMap(
convertSet(applications, application -> Long.valueOf(application.getCreator())));
// 2. 转换并拼接 VO
return convertList(applications, application -> {
OaLeaveApplyRespVO vo = BeanUtils.toBean(application, OaLeaveApplyRespVO.class);
MapUtils.findAndThen(users, Long.valueOf(application.getCreator()), user -> vo.setCreatorName(user.getNickname()));
return vo;
});
}
}
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.oa.controller.admin.leave.vo;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - 请假申请分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaLeaveApplyPageReqVO extends PageParam {
@Schema(description = "标题", example = "个人事务请假申请")
private String title;
@Schema(description = "审批状态", example = "-1")
private Integer status;
}
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.oa.controller.admin.leave.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 请假申请 Response VO")
@Data
public class OaLeaveApplyRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "标题", example = "个人事务请假申请")
private String title;
@Schema(description = "紧急程度", example = "1")
private Integer urgency;
@Schema(description = "请假类型", example = "1")
private Integer type;
@Schema(description = "开始时间", type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime startTime;
@Schema(description = "结束时间", type = "integer", format = "int64", example = "1789380000000")
private LocalDateTime endTime;
@Schema(description = "天数", example = "1")
private Integer days;
@Schema(description = "申请原因", example = "处理个人事务")
private String reason;
@Schema(description = "审批状态", example = "-1")
private Integer status;
@Schema(description = "流程实例编号", example = "1024")
private String processInstanceId;
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
private List<String> fileUrls;
@Schema(description = "申请人编号", example = "1024")
private String creator;
@Schema(description = "申请人昵称", example = "张三")
private String creatorName;
@Schema(description = "申请时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime createTime;
}
@@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.oa.controller.admin.leave.vo;
import cn.iocoder.yudao.framework.dict.validation.InDict;
import cn.iocoder.yudao.module.oa.enums.DictTypeConstants;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 请假申请新增/修改 Request VO")
@Data
public class OaLeaveApplySaveReqVO {
@Schema(description = "申请编号", example = "1024")
private Long id;
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "个人事务请假申请")
@NotBlank(message = "标题不能为空")
@Size(max = 255, message = "标题不能超过 255 个字符")
private String title;
@Schema(description = "紧急程度", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "紧急程度不能为空")
@InDict(type = DictTypeConstants.APPLY_URGENCY, message = "紧急程度必须是 {value}")
private Integer urgency;
@Schema(description = "请假类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "请假类型不能为空")
@InDict(type = DictTypeConstants.LEAVE_TYPE, message = "请假类型必须是 {value}")
private Integer type;
@Schema(description = "开始时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789347600000")
@NotNull(message = "开始时间不能为空")
private LocalDateTime startTime;
@Schema(description = "结束时间", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64", example = "1789380000000")
@NotNull(message = "结束时间不能为空")
private LocalDateTime endTime;
@Schema(description = "申请原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "处理个人事务")
@NotBlank(message = "申请原因不能为空")
@Size(max = 5000, message = "申请原因不能超过 5000 个字符")
private String reason;
@Schema(description = "附件地址列表", example = "[\"https://example.com/file.pdf\"]")
@Size(max = 5, message = "附件不能超过 5 个")
private List<@NotBlank(message = "附件地址不能为空") @Size(max = 1024, message = "附件地址不能超过 1024 个字符") String> fileUrls;
@AssertTrue(message = "结束时间不能早于开始时间")
@JsonIgnore
public boolean isTimeRangeValid() {
// 缺失值交由字段上的必填校验处理
return startTime == null || endTime == null || !endTime.isBefore(startTime);
}
}
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.oa.controller.admin.leave.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - 请假申请提交 Request VO")
@Data
public class OaLeaveApplySubmitReqVO {
@Schema(description = "申请编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "申请编号不能为空")
private Long id;
@Schema(description = "发起人自选审批人")
private Map<String, List<Long>> startUserSelectAssignees;
}
@@ -0,0 +1,132 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail;
import cn.iocoder.yudao.framework.apilog.core.annotation.ApiAccessLog;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.account.*;
import cn.iocoder.yudao.module.oa.service.mail.OaMailAccountService;
import cn.iocoder.yudao.module.oa.dal.dataobject.mail.OaMailAccountDO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import java.util.Set;
import cn.hutool.core.util.StrUtil;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "管理后台 - 企业邮箱账号")
@RestController
@RequestMapping("/oa/mail-account")
@Validated
public class OaMailAccountController {
@Resource
private OaMailAccountService mailAccountService;
@Resource
private AdminUserApi adminUserApi;
@GetMapping("/list")
@Operation(summary = "获得本人邮箱账号列表")
@Parameter(name = "status", description = "状态,未传时查询全部状态")
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
public CommonResult<List<OaMailAccountRespVO>> getMailAccountList(@RequestParam(value = "status", required = false) Integer status) {
List<OaMailAccountDO> accounts = mailAccountService.getMailAccountListByUserIdAndStatus(getLoginUserId(), status);
return success(BeanUtils.toBean(accounts, OaMailAccountRespVO.class));
}
@GetMapping("/simple-list")
@Operation(summary = "获得当前租户邮箱地址精简列表")
public CommonResult<List<OaMailAccountRespVO>> getSimpleMailAccountList() {
// 1.1 查询邮箱及归属人,批量获取用户展示信息
List<OaMailAccountDO> accounts = mailAccountService.getMailAccountListByStatus(CommonStatusEnum.ENABLE.getStatus());
Map<Long, AdminUserRespDTO> users = adminUserApi.getUserMap(
convertSet(accounts, account -> NumberUtils.parseLong(account.getCreator())));
// 1.2 全员选择器只返回邮箱和姓名,不暴露登录名及连接配置
List<OaMailAccountRespVO> result = convertList(accounts, account -> {
OaMailAccountRespVO respVO = new OaMailAccountRespVO().setMail(account.getMail());
MapUtils.findAndThen(users, NumberUtils.parseLong(account.getCreator()),
user -> respVO.setUserName(user.getNickname()));
return respVO;
});
// 2. 补充已配置邮箱的启用用户
Set<String> mails = convertSet(result, account -> account.getMail().toLowerCase(java.util.Locale.ROOT));
List<AdminUserRespDTO> systemUsers = adminUserApi.getUserListByStatus(CommonStatusEnum.ENABLE.getStatus()).getCheckedData();
for (AdminUserRespDTO user : systemUsers) {
if (StrUtil.isNotBlank(user.getEmail()) && mails.add(user.getEmail().toLowerCase(java.util.Locale.ROOT))) {
result.add(new OaMailAccountRespVO().setMail(user.getEmail()).setUserName(user.getNickname()));
}
}
return success(result);
}
@GetMapping("/get")
@Operation(summary = "获得本人邮箱账号")
@Parameter(name = "id", description = "邮箱账号编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
public CommonResult<OaMailAccountRespVO> getMailAccount(@RequestParam("id") Long id) {
OaMailAccountDO account = mailAccountService.validateMailAccount(id, getLoginUserId());
return success(BeanUtils.toBean(account, OaMailAccountRespVO.class));
}
@PostMapping("/create")
@ApiAccessLog(requestEnable = false) // 特殊:password 密码,避免日志记录
@Operation(summary = "绑定本人邮箱账号")
@PreAuthorize("@ss.hasPermission('oa:mail-account:create')")
public CommonResult<Long> createMailAccount(@Valid @RequestBody OaMailAccountSaveReqVO reqVO) {
return success(mailAccountService.createMailAccount(reqVO, getLoginUserId()));
}
@PutMapping("/update")
@ApiAccessLog(requestEnable = false) // 特殊:password 密码,避免日志记录
@Operation(summary = "更新本人邮箱账号")
@PreAuthorize("@ss.hasPermission('oa:mail-account:update')")
public CommonResult<Boolean> updateMailAccount(@Valid @RequestBody OaMailAccountSaveReqVO reqVO) {
mailAccountService.updateMailAccount(reqVO, getLoginUserId());
return success(true);
}
@PutMapping("/update-default")
@Operation(summary = "设置本人默认发件账号")
@Parameter(name = "id", description = "邮箱账号编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:mail-account:update')")
public CommonResult<Boolean> updateMailAccountDefault(@RequestParam("id") Long id) {
mailAccountService.updateMailAccountDefault(id, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "移除本人邮箱绑定")
@Parameter(name = "id", description = "邮箱账号编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:mail-account:delete')")
public CommonResult<Boolean> deleteMailAccount(@RequestParam("id") Long id) {
mailAccountService.deleteMailAccount(id, getLoginUserId());
return success(true);
}
@PostMapping("/test-connection")
@Operation(summary = "测试本人邮箱连接")
@Parameter(name = "id", description = "邮箱账号编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
public CommonResult<Map<String, Boolean>> testMailAccountConnection(@RequestParam("id") Long id) {
return success(mailAccountService.testMailAccountConnection(id, getLoginUserId()));
}
}
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message.OaMailFolderRespVO;
import cn.iocoder.yudao.module.oa.service.mail.OaMailMessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 企业邮箱文件夹")
@RestController
@RequestMapping("/oa/mail-folder")
@Validated
public class OaMailFolderController {
@Resource
private OaMailMessageService mailMessageService;
@GetMapping("/list")
@Operation(summary = "获得邮箱文件夹列表")
@Parameter(name = "accountId", description = "邮箱账号编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
public CommonResult<List<OaMailFolderRespVO>> getMailFolderList(
@RequestParam Long accountId) {
return success(mailMessageService.getMailFolderList(accountId, getLoginUserId()));
}
}
@@ -0,0 +1,131 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail;
import cn.iocoder.yudao.framework.common.pojo.*;
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message.*;
import cn.iocoder.yudao.module.oa.dal.dataobject.mail.*;
import cn.iocoder.yudao.module.oa.service.mail.OaMailMessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import cn.hutool.core.lang.Pair;
import cn.iocoder.yudao.framework.common.util.servlet.ServletUtils;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.List;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.enums.mail.OaMailComposeModeEnum;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 企业邮箱邮件")
@RestController
@RequestMapping("/oa/mail-message")
@Validated
@PreAuthorize("@ss.hasPermission('oa:mail-account:query')")
public class OaMailMessageController {
@Resource
private OaMailMessageService mailMessageService;
@GetMapping("/compose")
@Operation(summary = "获得写信预填数据")
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
@Parameter(name = "mode", description = "写信方式", required = true)
public CommonResult<OaMailMessageSaveReqVO> getMailMessageCompose(@RequestParam Long id,
@RequestParam @InEnum(value = OaMailComposeModeEnum.class, message = "写信方式必须是 {value}") String mode) {
return success(mailMessageService.getMailMessageCompose(id, mode, getLoginUserId()));
}
@PostMapping("/sync")
@Operation(summary = "同步本人邮箱索引")
@Parameter(name = "accountId", description = "邮箱账号编号", required = true, example = "1024")
public CommonResult<Integer> syncMailMessageList(@RequestParam Long accountId) {
return success(mailMessageService.syncMailMessageList(accountId, getLoginUserId()));
}
@GetMapping("/page")
@Operation(summary = "获得邮件索引分页")
public CommonResult<PageResult<OaMailMessageRespVO>> getMailMessagePage(@Valid OaMailMessagePageReqVO reqVO) {
PageResult<OaMailMessageDO> pageResult = mailMessageService.getMailMessagePage(reqVO, getLoginUserId());
return success(BeanUtils.toBean(pageResult, OaMailMessageRespVO.class));
}
@GetMapping("/get")
@Operation(summary = "获得邮件详情")
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
public CommonResult<OaMailMessageRespVO> getMailMessage(@RequestParam Long id) {
OaMailMessageDO message = mailMessageService.getMailMessage(id, getLoginUserId());
return success(BeanUtils.toBean(message, OaMailMessageRespVO.class)
.setAttachments(BeanUtils.toBean(message.getAttachments(), OaMailMessageRespVO.Attachment.class)));
}
@PutMapping("/update-read")
@Operation(summary = "更新邮件已读状态")
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
@Parameter(name = "readStatus", description = "是否已读", required = true)
public CommonResult<Boolean> updateMailMessageRead(@RequestParam Long id,
@RequestParam Boolean readStatus) {
mailMessageService.updateMailMessageRead(id, readStatus, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除邮件")
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
public CommonResult<Boolean> deleteMailMessage(@RequestParam Long id) {
mailMessageService.deleteMailMessage(id, getLoginUserId());
return success(true);
}
@PutMapping("/restore")
@Operation(summary = "恢复邮件到收件箱")
@Parameter(name = "id", description = "邮件编号", required = true, example = "1024")
public CommonResult<Boolean> restoreMailMessage(@RequestParam Long id) {
mailMessageService.restoreMailMessage(id, getLoginUserId());
return success(true);
}
@PostMapping(value = "/save-draft", consumes = "application/json")
@Operation(summary = "保存草稿")
public CommonResult<Long> saveMailMessageDraft(@Valid @RequestBody OaMailMessageSaveReqVO reqVO) {
return success(mailMessageService.saveMailMessageDraft(reqVO, getLoginUserId()));
}
@PostMapping(value = "/send", consumes = "application/json")
@Operation(summary = "发送邮件")
public CommonResult<String> sendMailMessage(@Valid @RequestBody OaMailMessageSaveReqVO reqVO) {
return success(mailMessageService.sendMailMessage(reqVO, getLoginUserId()));
}
@PostMapping(value = "/save-draft", consumes = "multipart/form-data")
@Operation(summary = "保存带附件的草稿")
public CommonResult<Long> saveMailMessageDraftWithFiles(@Valid @RequestPart("data") OaMailMessageSaveReqVO reqVO,
@RequestPart(value = "files", required = false) List<MultipartFile> files) {
return success(mailMessageService.saveMailMessageDraft(reqVO.setFiles(files), getLoginUserId()));
}
@PostMapping(value = "/send", consumes = "multipart/form-data")
@Operation(summary = "发送带附件的邮件")
public CommonResult<String> sendMailMessageWithFiles(@Valid @RequestPart("data") OaMailMessageSaveReqVO reqVO,
@RequestPart(value = "files", required = false) List<MultipartFile> files) {
return success(mailMessageService.sendMailMessage(reqVO.setFiles(files), getLoginUserId()));
}
@GetMapping("/attachment")
@Operation(summary = "下载本人邮件附件")
public void getMailMessageAttachment(@RequestParam Long id, @RequestParam String part,
HttpServletResponse response) throws IOException {
Pair<String, byte[]> attachment = mailMessageService.getMailMessageAttachment(id, part, getLoginUserId());
ServletUtils.writeAttachment(response, attachment.getKey(), attachment.getValue());
}
}
@@ -0,0 +1,83 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.provider.OaMailProviderRespVO;
import cn.iocoder.yudao.module.oa.controller.admin.mail.vo.provider.OaMailProviderSaveReqVO;
import cn.iocoder.yudao.module.oa.dal.dataobject.mail.OaMailProviderDO;
import cn.iocoder.yudao.module.oa.service.mail.OaMailProviderService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "管理后台 - 企业邮箱服务配置")
@RestController
@RequestMapping("/oa/mail-provider")
@Validated
public class OaMailProviderController {
@Resource
private OaMailProviderService mailProviderService;
@GetMapping("/list")
@Operation(summary = "获得邮箱服务配置列表")
@Parameter(name = "status", description = "状态,未传时查询全部状态")
@PreAuthorize("@ss.hasPermission('oa:mail-provider:query')")
public CommonResult<List<OaMailProviderRespVO>> getMailProviderList(@RequestParam(value = "status", required = false) Integer status) {
List<OaMailProviderDO> providers = mailProviderService.getMailProviderList(status);
return success(BeanUtils.toBean(providers, OaMailProviderRespVO.class));
}
@GetMapping("/simple-list")
@Operation(summary = "获得邮箱服务配置精简列表")
public CommonResult<List<OaMailProviderRespVO>> getSimpleMailProviderList() {
List<OaMailProviderDO> providers = mailProviderService.getMailProviderList(CommonStatusEnum.ENABLE.getStatus());
return success(convertList(providers, provider -> new OaMailProviderRespVO()
.setId(provider.getId()).setName(provider.getName()).setStatus(provider.getStatus())));
}
@GetMapping("/get")
@Operation(summary = "获得邮箱服务配置")
@Parameter(name = "id", description = "服务配置编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:mail-provider:query')")
public CommonResult<OaMailProviderRespVO> getMailProvider(@RequestParam("id") Long id) {
OaMailProviderDO provider = mailProviderService.validateMailProviderExists(id);
return success(BeanUtils.toBean(provider, OaMailProviderRespVO.class));
}
@PostMapping("/create")
@Operation(summary = "创建邮箱服务配置")
@PreAuthorize("@ss.hasPermission('oa:mail-provider:create')")
public CommonResult<Long> createMailProvider(@Valid @RequestBody OaMailProviderSaveReqVO reqVO) {
return success(mailProviderService.createMailProvider(reqVO));
}
@PutMapping("/update")
@Operation(summary = "更新邮箱服务配置")
@PreAuthorize("@ss.hasPermission('oa:mail-provider:update')")
public CommonResult<Boolean> updateMailProvider(@Valid @RequestBody OaMailProviderSaveReqVO reqVO) {
mailProviderService.updateMailProvider(reqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除邮箱服务配置")
@Parameter(name = "id", description = "服务配置编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:mail-provider:delete')")
public CommonResult<Boolean> deleteMailProvider(@RequestParam("id") Long id) {
mailProviderService.deleteMailProvider(id);
return success(true);
}
}
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.account;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Data
@Schema(description = "管理后台 - 企业邮箱账号 Response VO")
public class OaMailAccountRespVO {
@Schema(description = "账号编号", example = "1024")
private Long id;
@Schema(description = "服务配置编号", example = "1024")
private Long providerId;
@Schema(description = "邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user@example.com")
private String mail;
@Schema(description = "归属人姓名", example = "张三")
private String userName;
@Schema(description = "登录用户名", example = "user@example.com")
private String username;
@Schema(description = "是否默认发件账号", example = "false")
private Boolean defaultStatus;
@Schema(description = "状态", example = "0")
private Integer status;
}
@@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.account;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.ToString;
@Data
@Schema(description = "管理后台 - 企业邮箱账号新增/修改 Request VO")
public class OaMailAccountSaveReqVO {
@Schema(description = "账号编号", example = "1024")
private Long id;
@Schema(description = "服务配置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "服务配置编号不能为空")
private Long providerId;
@Schema(description = "邮箱地址", requiredMode = Schema.RequiredMode.REQUIRED, example = "user@example.com")
@NotBlank(message = "邮箱地址不能为空")
@Email(message = "邮箱地址格式不正确")
@Size(max = 255, message = "邮箱地址长度不能超过 {max} 个字符")
private String mail;
@Schema(description = "登录用户名", requiredMode = Schema.RequiredMode.REQUIRED, example = "user@example.com")
@NotBlank(message = "登录用户名不能为空")
@Size(max = 255, message = "登录用户名长度不能超过 {max} 个字符")
private String username;
@Schema(description = "密码或授权码,新增时必填,修改时留空表示不变", accessMode = Schema.AccessMode.WRITE_ONLY)
@ToString.Exclude
@Size(max = 4096, message = "密码或授权码长度不能超过 {max} 个字符")
private String password;
@Schema(description = "是否默认发件账号", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否默认发件账号不能为空")
private Boolean defaultStatus;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
private Integer status;
/**
* 校验默认邮箱账号的启用状态
*
* @return 是否符合默认账号规则
*/
@AssertTrue(message = "默认邮箱账号必须启用")
@JsonIgnore
public boolean isDefaultStatusValid() {
return !Boolean.TRUE.equals(defaultStatus) || CommonStatusEnum.ENABLE.getStatus().equals(status);
}
}
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 邮件目录 Response VO")
@Data
public class OaMailFolderRespVO {
@Schema(description = "目录查询标识", example = "INBOX")
private String key;
@Schema(description = "显示名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "收件箱")
private String name;
@Schema(description = "未读数量", example = "1")
private Long unreadCount;
}
@@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.oa.enums.mail.OaMailFolderKeyEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - 邮件分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaMailMessagePageReqVO extends PageParam {
@Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "邮箱账号编号不能为空")
private Long accountId;
@Schema(description = "文件夹类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "INBOX")
@NotBlank(message = "文件夹标识不能为空")
private String folderKey;
@Schema(description = "主题或发件人", example = "项目评审")
@Size(max = 200, message = "搜索关键字不能超过 200 个字符")
private String keyword;
@Schema(description = "是否已读", example = "false")
private Boolean readStatus;
@Schema(description = "是否有附件", example = "false")
private Boolean hasAttach;
/**
* 校验文件夹入口
*
* @return 是否为标准入口或合法的自定义目录编号
*/
@AssertTrue(message = "文件夹标识不正确")
@JsonIgnore
public boolean isFolderKeyValid() {
if (StrUtil.isBlank(folderKey) || ArrayUtil.contains(OaMailFolderKeyEnum.ARRAYS, folderKey)) {
return true; // 空值由 NotBlank 校验
}
return folderKey.matches("[1-9][0-9]{0,17}");
}
}
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 企业邮箱邮件 Response VO")
@Data
public class OaMailMessageRespVO {
@Schema(description = "邮件编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long accountId;
@Schema(description = "文件夹编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long folderId;
@Schema(description = "远端邮件 UID", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
private Long uid;
@Schema(description = "远端 UID 有效期", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
private Long uidValidity;
@Schema(description = "邮件主题", requiredMode = Schema.RequiredMode.REQUIRED, example = "项目评审会议通知")
private String subject;
@Schema(description = "发件人", example = "sender@example.com")
private String sender;
@Schema(description = "收件人", example = "[\"recipient@example.com\"]")
private List<String> recipients;
@Schema(description = "抄送人", example = "[\"cc@example.com\"]")
private List<String> ccs;
@Schema(description = "回复地址", example = "[\"reply@example.com\"]")
private List<String> replyTos;
@Schema(description = "接收时间", type = "integer", format = "int64", example = "1789347600000")
private LocalDateTime receiveTime;
@Schema(description = "是否已读", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean readStatus;
@Schema(description = "是否有附件", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
private Boolean hasAttach;
@Schema(description = "邮件大小(字节)", example = "1024")
private Integer size;
@Schema(description = "安全 HTML 正文", example = "<p>请查收本周工作安排。</p>")
private String content;
@Schema(description = "附件信息")
private List<Attachment> attachments;
@Schema(description = "管理后台 - 邮件附件 Response VO")
@Data
public static class Attachment {
@Schema(description = "MIME 部件路径", example = "1.2")
private String part;
@Schema(description = "附件名称", example = "办公用品盘点结果.pdf")
private String name;
@Schema(description = "附件大小(字节)", example = "1024")
private Integer size;
}
}
@@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.message;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonIgnore;
import org.springframework.web.multipart.MultipartFile;
import cn.iocoder.yudao.module.oa.dal.dataobject.mail.OaMailMessageDO;
import java.util.List;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.mail.OaMailComposeModeEnum;
@Schema(description = "管理后台 - 写信及草稿 Request VO")
@Data
public class OaMailMessageSaveReqVO {
@Schema(description = "邮箱账号编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "邮箱账号编号不能为空")
private Long accountId;
@Schema(description = "原草稿编号", example = "1024")
private Long draftId;
@Schema(description = "收件人", example = "[\"recipient@example.com\"]")
private List<@NotBlank(message = "收件人地址不能为空") String> recipients;
@Schema(description = "抄送人", example = "[\"cc@example.com\"]")
private List<@NotBlank(message = "抄送人地址不能为空") String> ccs;
@Schema(description = "主题", example = "项目评审会议通知")
@Size(max = 65535, message = "邮件主题不能超过 65535 个字符")
private String subject;
@Schema(description = "正文 HTML", example = "<p>请查收本周工作安排。</p>")
@Size(max = 500000, message = "正文 HTML长度不能超过 {max} 个字符")
private String content;
@Schema(description = "回复或转发的原邮件编号", example = "1024")
private Long sourceId;
@Schema(description = "写信方式", requiredMode = Schema.RequiredMode.REQUIRED, example = "new")
@InEnum(value = OaMailComposeModeEnum.class, message = "写信方式必须是 {value}")
@NotBlank(message = "写信方式不能为空")
private String mode;
@Schema(description = "保留的原邮件附件路径;空列表表示移除全部原附件")
private List<String> attachmentParts;
@Schema(description = "原邮件附件,仅用于写信回显", accessMode = Schema.AccessMode.READ_ONLY)
private List<OaMailMessageDO.Attachment> attachments;
@JsonIgnore
@Schema(hidden = true)
private List<MultipartFile> files;
}
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.provider;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - 邮箱服务配置 Response VO")
@Data
public class OaMailProviderRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
private Long id;
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "企业邮箱")
private String name;
@Schema(description = "IMAP 连接配置")
private OaMailProviderSaveReqVO.ConnectionConfig imap;
@Schema(description = "SMTP 连接配置")
private OaMailProviderSaveReqVO.ConnectionConfig smtp;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
private Integer status;
}
@@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.oa.controller.admin.mail.vo.provider;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.Data;
import com.fasterxml.jackson.annotation.JsonIgnore;
@Data
@Schema(description = "管理后台 - 企业邮箱服务配置新增/修改 Request VO")
public class OaMailProviderSaveReqVO {
@Schema(description = "服务配置编号", example = "1024")
private Long id;
@Schema(description = "名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "企业邮箱")
@NotBlank(message = "名称不能为空")
@Size(max = 255, message = "名称长度不能超过 {max} 个字符")
private String name;
@Schema(description = "IMAP 连接配置", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "IMAP 连接配置不能为空")
@Valid
private ConnectionConfig imap;
@Schema(description = "SMTP 连接配置", requiredMode = Schema.RequiredMode.REQUIRED)
@NotNull(message = "SMTP 连接配置不能为空")
@Valid
private ConnectionConfig smtp;
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "状态不能为空")
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
private Integer status;
@Schema(description = "管理后台 - 邮箱连接配置 Request VO")
@Data
public static class ConnectionConfig {
@Schema(description = "服务器域名", requiredMode = Schema.RequiredMode.REQUIRED, example = "imap.example.com")
@NotBlank(message = "服务器域名不能为空")
@Size(max = 253, message = "服务器域名长度不能超过 253 个字符")
private String host;
@Schema(description = "服务器端口", requiredMode = Schema.RequiredMode.REQUIRED, example = "993")
@NotNull(message = "服务器端口不能为空")
@Min(value = 1, message = "服务器端口不能小于 1")
@Max(value = 65535, message = "服务器端口不能大于 65535")
private Integer port;
@Schema(description = "是否开启 SSL", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "是否开启 SSL 不能为空")
private Boolean sslEnable;
@Schema(description = "是否开启 STARTTLS", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "是否开启 STARTTLS 不能为空")
private Boolean starttlsEnable;
/**
* 校验邮箱连接加密方式
*
* @return 是否仅启用一种加密方式,空值由字段必填注解校验
*/
@AssertTrue(message = "请选择 SSL 或 STARTTLS,且不能同时开启")
@JsonIgnore
public boolean isEncryptionValid() {
return sslEnable == null || !sslEnable.equals(starttlsEnable);
}
}
}
@@ -0,0 +1,184 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom;
import cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking.*;
import cn.iocoder.yudao.module.system.api.dept.DeptApi;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.dal.dataobject.meetingroom.*;
import cn.iocoder.yudao.module.oa.service.meetingroom.*;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 会议室预定")
@RestController
@RequestMapping("/oa/meeting-room-booking")
@Validated
public class OaMeetingRoomBookingController {
@Resource
private OaMeetingRoomBookingService meetingRoomBookingService;
@Resource
private OaMeetingRoomService meetingRoomService;
@Resource
private AdminUserApi adminUserApi;
@Resource
private DeptApi deptApi;
@PostMapping("/create")
@Operation(summary = "创建会议室预定草稿")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:create')")
public CommonResult<Long> createMeetingRoomBooking(@Valid @RequestBody OaMeetingRoomBookingSaveReqVO reqVO) {
return success(meetingRoomBookingService.createMeetingRoomBooking(reqVO, getLoginUserId()));
}
@PutMapping("/update")
@Operation(summary = "更新会议室预定草稿")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:update')")
public CommonResult<Boolean> updateMeetingRoomBooking(@Valid @RequestBody OaMeetingRoomBookingSaveReqVO reqVO) {
meetingRoomBookingService.updateMeetingRoomBooking(reqVO, getLoginUserId());
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除会议室预定草稿")
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:delete')")
public CommonResult<Boolean> deleteMeetingRoomBooking(@RequestParam("id") Long id) {
meetingRoomBookingService.deleteMeetingRoomBooking(id, getLoginUserId());
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得会议室预定详情")
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:query')")
public CommonResult<OaMeetingRoomBookingRespVO> getMeetingRoomBooking(@RequestParam("id") Long id) {
OaMeetingRoomBookingDO booking = meetingRoomBookingService.getMeetingRoomBooking(id);
return success(buildMeetingRoomBookingRespVO(booking));
}
@GetMapping("/page")
@Operation(summary = "获得本人的会议室预定分页")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:query')")
public CommonResult<PageResult<OaMeetingRoomBookingRespVO>> getMeetingRoomBookingPage(@Valid OaMeetingRoomBookingPageReqVO reqVO) {
PageResult<OaMeetingRoomBookingDO> pageResult = meetingRoomBookingService.getMeetingRoomBookingPage(getLoginUserId(), reqVO);
return success(new PageResult<>(buildMeetingRoomBookingRespVOList(pageResult.getList()), pageResult.getTotal()));
}
@PutMapping("/submit")
@Operation(summary = "提交会议室预定")
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:create')")
public CommonResult<Boolean> submitMeetingRoomBooking(@RequestParam("id") Long id) {
meetingRoomBookingService.submitMeetingRoomBooking(id, getLoginUserId());
return success(true);
}
@PutMapping("/cancel")
@Operation(summary = "取消会议室预定")
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:update')")
public CommonResult<Boolean> cancelMeetingRoomBooking(@RequestParam("id") Long id) {
meetingRoomBookingService.cancelMeetingRoomBooking(id, getLoginUserId());
return success(true);
}
@PutMapping("/start")
@Operation(summary = "开始使用会议室预定")
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:update')")
public CommonResult<Boolean> startMeetingRoomBooking(@RequestParam("id") Long id) {
meetingRoomBookingService.startMeetingRoomBooking(id, getLoginUserId());
return success(true);
}
@PutMapping("/finish")
@Operation(summary = "完成使用会议室预定")
@Parameter(name = "id", description = "预定编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:meeting-room-booking:update')")
public CommonResult<Boolean> finishMeetingRoomBooking(@RequestParam("id") Long id) {
meetingRoomBookingService.finishMeetingRoomBooking(id, getLoginUserId());
return success(true);
}
@GetMapping("/schedule")
@Operation(summary = "获得会议室日程")
@PreAuthorize("@ss.hasAnyPermissions('oa:meeting-room:query', 'oa:meeting-room-booking:query')")
public CommonResult<List<OaMeetingRoomBookingRespVO>> getMeetingRoomBookingListBySchedule(@Valid OaMeetingRoomBookingScheduleReqVO reqVO) {
List<OaMeetingRoomBookingDO> bookings = meetingRoomBookingService.getMeetingRoomBookingListBySchedule(reqVO);
return success(buildMeetingRoomBookingRespVOList(bookings));
}
// ==================== 拼接 VO ====================
/**
* 拼接会议室预定详情
*
* @param booking 会议室预定
* @return 会议室预定响应
*/
private OaMeetingRoomBookingRespVO buildMeetingRoomBookingRespVO(OaMeetingRoomBookingDO booking) {
if (booking == null) {
return null;
}
return CollUtil.getFirst(buildMeetingRoomBookingRespVOList(Collections.singletonList(booking)));
}
/**
* 构建会议室预定响应列表
*
* @param bookings 会议室预定列表
* @return 响应列表
*/
private List<OaMeetingRoomBookingRespVO> buildMeetingRoomBookingRespVOList(List<OaMeetingRoomBookingDO> bookings) {
if (CollUtil.isEmpty(bookings)) {
return Collections.emptyList();
}
// 1.1 批量查询申请人、主持人和参会人
Set<Long> userIds = convertSet(bookings, OaMeetingRoomBookingDO::getModeratorUserId);
userIds.addAll(convertSet(bookings, booking -> Long.valueOf(booking.getCreator()),
booking -> NumberUtil.isLong(booking.getCreator())));
userIds.addAll(convertSetByFlatMap(bookings, OaMeetingRoomBookingDO::getAttendeeUserIds, Collection::stream));
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
// 1.2 批量查询房间及申请部门
Map<Long, OaMeetingRoomDO> roomMap = meetingRoomService.getMeetingRoomMap(
convertSet(bookings, OaMeetingRoomBookingDO::getRoomId));
Map<Long, DeptRespDTO> deptMap = deptApi.getDeptMap(convertSet(bookings, OaMeetingRoomBookingDO::getDeptId));
// 2. 转换并拼接展示字段
return convertList(bookings, booking -> {
OaMeetingRoomBookingRespVO vo = BeanUtils.toBean(booking, OaMeetingRoomBookingRespVO.class);
MapUtils.findAndThen(roomMap, booking.getRoomId(), room ->
vo.setRoomName(room.getName()).setRoomLocation(room.getLocation()).setRoomType(room.getType()));
MapUtils.findAndThen(userMap, booking.getModeratorUserId(), user -> vo.setModeratorName(user.getNickname()));
if (NumberUtil.isLong(booking.getCreator())) {
MapUtils.findAndThen(userMap, Long.valueOf(booking.getCreator()), user -> vo.setCreatorName(user.getNickname()));
}
MapUtils.findAndThen(deptMap, booking.getDeptId(), dept -> vo.setDeptName(dept.getName()));
vo.setAttendeeNames(convertList(booking.getAttendeeUserIds(), userId ->
userMap.containsKey(userId) ? userMap.get(userId).getNickname() : userId.toString()));
return vo;
});
}
}
@@ -0,0 +1,130 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom;
import cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.room.*;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.oa.dal.dataobject.meetingroom.*;
import cn.iocoder.yudao.module.oa.service.meetingroom.*;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - 会议室")
@RestController
@RequestMapping("/oa/meeting-room")
@Validated
public class OaMeetingRoomController {
@Resource
private OaMeetingRoomService meetingRoomService;
@Resource
private AdminUserApi adminUserApi;
@PostMapping("/create")
@Operation(summary = "创建会议室")
@PreAuthorize("@ss.hasPermission('oa:meeting-room:create')")
public CommonResult<Long> createMeetingRoom(@Valid @RequestBody OaMeetingRoomSaveReqVO reqVO) {
return success(meetingRoomService.createMeetingRoom(reqVO));
}
@PutMapping("/update")
@Operation(summary = "更新会议室")
@PreAuthorize("@ss.hasPermission('oa:meeting-room:update')")
public CommonResult<Boolean> updateMeetingRoom(@Valid @RequestBody OaMeetingRoomSaveReqVO reqVO) {
meetingRoomService.updateMeetingRoom(reqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除会议室")
@Parameter(name = "id", description = "会议室编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:meeting-room:delete')")
public CommonResult<Boolean> deleteMeetingRoom(@RequestParam("id") Long id) {
meetingRoomService.deleteMeetingRoom(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得会议室详情")
@Parameter(name = "id", description = "会议室编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('oa:meeting-room:query')")
public CommonResult<OaMeetingRoomRespVO> getMeetingRoom(@RequestParam("id") Long id) {
OaMeetingRoomDO room = meetingRoomService.validateMeetingRoomExists(id);
return success(buildMeetingRoomRespVO(room));
}
@GetMapping("/page")
@Operation(summary = "获得会议室分页")
@PreAuthorize("@ss.hasPermission('oa:meeting-room:query')")
public CommonResult<PageResult<OaMeetingRoomRespVO>> getMeetingRoomPage(@Valid OaMeetingRoomPageReqVO reqVO) {
PageResult<OaMeetingRoomDO> pageResult = meetingRoomService.getMeetingRoomPage(reqVO);
return success(new PageResult<>(buildMeetingRoomRespVOList(pageResult.getList()), pageResult.getTotal()));
}
@GetMapping("/bookable-page")
@Operation(summary = "获得可预定会议室分页")
public CommonResult<PageResult<OaMeetingRoomRespVO>> getMeetingRoomPageByBookingUserId(@Valid OaMeetingRoomPageReqVO reqVO) {
PageResult<OaMeetingRoomDO> pageResult = meetingRoomService.getMeetingRoomPageByBookingUserId(reqVO, getLoginUserId());
List<OaMeetingRoomRespVO> list = buildMeetingRoomRespVOList(pageResult.getList());
return success(new PageResult<>(convertList(list, room -> new OaMeetingRoomRespVO()
.setId(room.getId()).setName(room.getName()).setLocation(room.getLocation())
.setType(room.getType()).setStatus(room.getStatus()).setSeatCount(room.getSeatCount())
.setManagerName(room.getManagerName())), pageResult.getTotal()));
}
// ==================== 拼接 VO ====================
/**
* 构建会议室响应
*
* @param room 会议室
* @return 会议室响应
*/
private OaMeetingRoomRespVO buildMeetingRoomRespVO(OaMeetingRoomDO room) {
AdminUserRespDTO user = adminUserApi.getUser(room.getManagerUserId()).getCheckedData();
OaMeetingRoomRespVO respVO = BeanUtils.toBean(room, OaMeetingRoomRespVO.class);
if (user != null) {
respVO.setManagerName(user.getNickname()).setManagerPhone(user.getMobile());
}
return respVO;
}
/**
* 构建会议室响应列表
*
* @param rooms 会议室列表
* @return 响应列表
*/
private List<OaMeetingRoomRespVO> buildMeetingRoomRespVOList(List<OaMeetingRoomDO> rooms) {
if (CollUtil.isEmpty(rooms)) {
return Collections.emptyList();
}
// 1. 批量查询管理员
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(rooms, OaMeetingRoomDO::getManagerUserId));
// 2. 转换并拼接管理员姓名、电话
return convertList(rooms, room -> {
OaMeetingRoomRespVO vo = BeanUtils.toBean(room, OaMeetingRoomRespVO.class);
MapUtils.findAndThen(userMap, room.getManagerUserId(), user ->
vo.setManagerName(user.getNickname()).setManagerPhone(user.getMobile()));
return vo;
});
}
}
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 会议室预定分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaMeetingRoomBookingPageReqVO extends PageParam {
@Schema(description = "单据编号", example = "HY20260914000001")
private String no;
@Schema(description = "会议室名称", example = "101 项目讨论室")
private String roomName;
@Schema(description = "会议主题", example = "产品需求评审")
private String title;
@Schema(description = "主持人姓名", example = "芋艿")
private String moderatorName;
@Schema(description = "申请部门编号", example = "100")
private Long deptId;
@Schema(description = "审批状态", example = "2")
private Integer status;
@Schema(description = "使用状态", example = "0")
private Integer useStatus;
@Schema(description = "会议开始时间", example = "[\"2026-09-14 00:00:00\",\"2026-09-14 23:59:59\"]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] startTime;
@Schema(description = "会议结束时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] endTime;
@Schema(description = "创建人编号", example = "1")
private String creator;
@Schema(description = "创建时间", example = "[\"2026-09-14 00:00:00\",\"2026-09-14 23:59:59\"]")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}
@@ -0,0 +1,88 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 会议室预定 Response VO")
@Data
public class OaMeetingRoomBookingRespVO {
@Schema(description = "编号", example = "1024", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "会议室编号", example = "1024", requiredMode = Schema.RequiredMode.REQUIRED)
private Long roomId;
@Schema(description = "会议主题", example = "产品需求评审", requiredMode = Schema.RequiredMode.REQUIRED)
private String title;
@Schema(description = "会议开始时间", example = "1789351200000", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64")
private LocalDateTime startTime;
@Schema(description = "会议结束时间", example = "1789354800000", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64")
private LocalDateTime endTime;
@Schema(description = "主持人用户编号", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Long moderatorUserId;
@Schema(description = "参会人用户编号列表", example = "[1,2]")
private List<Long> attendeeUserIds;
@Schema(description = "会议提醒", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer reminderType;
@Schema(description = "会议说明", example = "讨论产品需求和排期")
private String description;
@Schema(description = "申请备注", example = "请提前安排会议室")
private String remark;
@Schema(description = "附件地址列表", example = "[\"https://www.iocoder.cn/meeting.pdf\"]")
private List<String> fileUrls;
@Schema(description = "申请单号", example = "HY20260914000001", requiredMode = Schema.RequiredMode.REQUIRED)
private String no;
@Schema(description = "会议室名称", example = "101 项目讨论室")
private String roomName;
@Schema(description = "位置", example = "办公楼一层 101")
private String roomLocation;
@Schema(description = "会议室类型", example = "1")
private Integer roomType;
@Schema(description = "主持人姓名", example = "芋艿")
private String moderatorName;
@Schema(description = "参会人姓名", example = "[\"芋艿\",\"小王\"]")
private List<String> attendeeNames;
@Schema(description = "申请人", example = "芋艿")
private String creatorName;
@Schema(description = "申请部门编号", example = "100")
private Long deptId;
@Schema(description = "部门名称", example = "研发部门")
private String deptName;
@Schema(description = "审批状态", example = "2", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer status;
@Schema(description = "使用状态", example = "0", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer useStatus;
@Schema(description = "提交时是否需要审批", example = "false")
private Boolean needApproval;
@Schema(description = "流程实例编号", example = "process-1024")
private String processInstanceId;
@Schema(description = "创建时间", example = "1789347600000", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64")
private LocalDateTime createTime;
}
@@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.meetingroom.*;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 会议室预定新增/修改 Request VO")
@Data
public class OaMeetingRoomBookingSaveReqVO {
@Schema(description = "编号", example = "1024")
private Long id;
@Schema(description = "会议室编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "会议室不能为空")
private Long roomId;
@Schema(description = "会议主题", requiredMode = Schema.RequiredMode.REQUIRED, example = "产品需求评审")
@NotBlank(message = "会议主题不能为空")
@Size(max = 200, message = "会议主题长度不能超过 {max} 个字符")
private String title;
@Schema(description = "会议开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1789351200000", type = "integer", format = "int64")
@NotNull(message = "开始时间不能为空")
private LocalDateTime startTime;
@Schema(description = "会议结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "1789354800000", type = "integer", format = "int64")
@NotNull(message = "结束时间不能为空")
private LocalDateTime endTime;
@Schema(description = "主持人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "主持人不能为空")
private Long moderatorUserId;
@Schema(description = "参会人用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
@NotNull(message = "参会人不能为空")
private List<@NotNull(message = "参会人不能为空") Long> attendeeUserIds;
@Schema(description = "会议提醒", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "会议提醒不能为空")
@InEnum(value = OaMeetingRoomReminderTypeEnum.class, message = "会议提醒不正确")
private Integer reminderType;
@Schema(description = "会议说明", example = "讨论产品需求和排期")
@Size(max = 500, message = "会议说明长度不能超过 {max} 个字符")
private String description;
@Schema(description = "申请备注", example = "请提前安排会议室")
@Size(max = 500, message = "申请备注长度不能超过 {max} 个字符")
private String remark;
@Schema(description = "附件地址列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[\"https://www.iocoder.cn/meeting.pdf\"]")
@NotNull(message = "附件地址列表不能为空")
private List<@NotBlank(message = "附件地址列表不能为空") String> fileUrls;
}
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.booking;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 会议室占用时段 Request VO")
@Data
public class OaMeetingRoomBookingScheduleReqVO {
@Schema(description = "会议室编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
@NotNull(message = "会议室编号不能为空")
private Long roomId;
@Schema(description = "查询开始时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-09-14 10:00:00")
@NotNull(message = "查询开始时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime startTime;
@Schema(description = "查询结束时间", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-09-14 11:00:00")
@NotNull(message = "查询结束时间不能为空")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime endTime;
}
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.room;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
@Schema(description = "管理后台 - 会议室分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
public class OaMeetingRoomPageReqVO extends PageParam {
@Schema(description = "会议室名称", example = "101 项目讨论室")
private String name;
@Schema(description = "位置", example = "办公楼一层 101")
private String location;
@Schema(description = "类型", example = "1")
private Integer type;
@Schema(description = "可用状态", example = "0")
private Integer status;
@Schema(description = "负责人姓名", example = "芋艿")
private String managerName;
}
@@ -0,0 +1,70 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.room;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - 会议室 Response VO")
@Data
public class OaMeetingRoomRespVO {
@Schema(description = "编号", example = "1024", requiredMode = Schema.RequiredMode.REQUIRED)
private Long id;
@Schema(description = "会议室名称", example = "101 项目讨论室", requiredMode = Schema.RequiredMode.REQUIRED)
private String name;
@Schema(description = "位置", example = "办公楼一层 101", requiredMode = Schema.RequiredMode.REQUIRED)
private String location;
@Schema(description = "会议室类型", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer type;
@Schema(description = "负责人用户编号", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Long managerUserId;
@Schema(description = "可用状态", example = "0", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer status;
@Schema(description = "会议室图片", example = "https://www.iocoder.cn/meeting-room.jpg")
private String picUrl;
@Schema(description = "坐席数", example = "8")
private Integer seatCount;
@Schema(description = "设备列表", example = "[1,2]")
private List<Integer> equipments;
@Schema(description = "允许预定", example = "true", requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean allowBooking;
@Schema(description = "预定需审批", example = "false", requiredMode = Schema.RequiredMode.REQUIRED)
private Boolean needApproval;
@Schema(description = "可预定范围", example = "0", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer bookingScope;
@Schema(description = "可预定成员用户编号列表", example = "[1,2]")
private List<Long> bookingUserIds;
@Schema(description = "显示顺序", example = "1", requiredMode = Schema.RequiredMode.REQUIRED)
private Integer sort;
@Schema(description = "备注", example = "请提前安排会议室")
private String remark;
@Schema(description = "附件地址列表", example = "[\"https://www.iocoder.cn/meeting.pdf\"]")
private List<String> fileUrls;
@Schema(description = "负责人姓名", example = "芋艿")
private String managerName;
@Schema(description = "负责人联系方式", example = "13800138000")
private String managerPhone;
@Schema(description = "创建时间", example = "1789347600000", requiredMode = Schema.RequiredMode.REQUIRED, type = "integer", format = "int64")
private LocalDateTime createTime;
}
@@ -0,0 +1,83 @@
package cn.iocoder.yudao.module.oa.controller.admin.meetingroom.vo.room;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.oa.enums.meetingroom.*;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.*;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - 会议室新增/修改 Request VO")
@Data
public class OaMeetingRoomSaveReqVO {
@Schema(description = "编号", example = "1024")
private Long id;
@Schema(description = "会议室名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "101 项目讨论室")
@NotBlank(message = "会议室名称不能为空")
@Size(max = 100, message = "会议室名称长度不能超过 {max} 个字符")
private String name;
@Schema(description = "位置", requiredMode = Schema.RequiredMode.REQUIRED, example = "办公楼一层 101")
@NotBlank(message = "位置不能为空")
@Size(max = 255, message = "位置长度不能超过 {max} 个字符")
private String location;
@Schema(description = "会议室类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "类型不能为空")
@InEnum(value = OaMeetingRoomTypeEnum.class, message = "会议室类型不正确")
private Integer type;
@Schema(description = "负责人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "负责人不能为空")
private Long managerUserId;
@Schema(description = "可用状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "可用状态不能为空")
@InEnum(value = OaMeetingRoomStatusEnum.class, message = "可用状态不正确")
private Integer status;
@Schema(description = "会议室图片", example = "https://www.iocoder.cn/meeting-room.jpg")
@Size(max = 1024, message = "会议室图片长度不能超过 {max} 个字符")
private String picUrl;
@Schema(description = "坐席数", example = "8")
@Min(value = 1, message = "坐席数不能小于 {value}")
private Integer seatCount;
@Schema(description = "设备列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
@NotNull(message = "设备列表不能为空")
private List<@NotNull(message = "设备列表不能为空") Integer> equipments;
@Schema(description = "允许预定", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
@NotNull(message = "允许预定不能为空")
private Boolean allowBooking;
@Schema(description = "预定需审批", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
@NotNull(message = "预定需审批不能为空")
private Boolean needApproval;
@Schema(description = "可预定范围", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
@NotNull(message = "可预定范围不能为空")
@InEnum(value = OaMeetingRoomBookingScopeEnum.class, message = "可预定范围不正确")
private Integer bookingScope;
@Schema(description = "可预定成员用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1,2]")
@NotNull(message = "可预定成员不能为空")
private List<@NotNull(message = "可预定成员不能为空") Long> bookingUserIds;
@Schema(description = "显示顺序", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "显示顺序不能为空")
private Integer sort;
@Schema(description = "备注", example = "请提前安排会议室")
@Size(max = 200, message = "备注长度不能超过 {max} 个字符")
private String remark;
@Schema(description = "附件地址列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[\"https://www.iocoder.cn/meeting.pdf\"]")
@NotNull(message = "附件地址列表不能为空")
private List<@NotBlank(message = "附件地址列表不能为空") String> fileUrls;
}

Some files were not shown because too many files have changed in this diff Show More