Compare commits
47 Commits
v2026.04(j
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
649362ea33 | |
|
|
10035e1709 | |
|
|
de134cf3b3 | |
|
|
c937f30436 | |
|
|
9431722bae | |
|
|
2ba231aa55 | |
|
|
0c261b4e02 | |
|
|
08c88e0cb3 | |
|
|
0ca9eda251 | |
|
|
1bc987805a | |
|
|
3020c6cdb0 | |
|
|
ffb4e8c158 | |
|
|
ff4ed31c1b | |
|
|
14add8edf0 | |
|
|
6e492e1e6b | |
|
|
7a4c1a0ba4 | |
|
|
9a319b68ff | |
|
|
931472e38d | |
|
|
47660ed156 | |
|
|
fbeec7d731 | |
|
|
8ad9167ddd | |
|
|
1b060dc93f | |
|
|
4363135bf8 | |
|
|
6d213c6746 | |
|
|
b8156e679d | |
|
|
d86fe5e5ca | |
|
|
38ec655d10 | |
|
|
9782e75514 | |
|
|
e7ac652a9c | |
|
|
17943589a4 | |
|
|
d67f91a63e | |
|
|
f6b769fc2d | |
|
|
36fbb9a68b | |
|
|
25a823fe82 | |
|
|
3314376e59 | |
|
|
050edb2db7 | |
|
|
5088b8c2e2 | |
|
|
8637b2a28f | |
|
|
1702fc1acb | |
|
|
d5ab0b06a7 | |
|
|
5cf473d48e | |
|
|
f194e7a3d3 | |
|
|
cf7b6d9cbc | |
|
|
9d1dd25bc7 | |
|
|
63cae8bc31 | |
|
|
b8cff89a12 | |
|
|
3b9ab7fa4a |
|
After Width: | Height: | Size: 169 KiB |
|
After Width: | Height: | Size: 72 KiB |
|
After Width: | Height: | Size: 62 KiB |
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 41 KiB |
|
After Width: | Height: | Size: 216 KiB |
|
After Width: | Height: | Size: 54 KiB |
31
README.md
|
|
@ -31,8 +31,8 @@
|
|||
| 【完整版】[yudao-cloud](https://gitee.com/zhijiantianya/yudao-cloud) | [`master`](https://gitee.com/zhijiantianya/yudao-cloud/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/zhijiantianya/yudao-cloud/tree/master-jdk17/) 分支 |
|
||||
| 【精简版】[yudao-cloud-mini](https://gitee.com/yudaocode/yudao-cloud-mini) | [`master`](https://gitee.com/yudaocode/yudao-cloud-mini/tree/master/) 分支 | [`master-jdk17`](https://gitee.com/yudaocode/yudao-cloud-mini/tree/master-jdk17/) 分支 |
|
||||
|
||||
* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、MES、AI 大模型、IoT 物联网 等功能
|
||||
* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、MES、AI 大模型、IoT 物联网 等功能
|
||||
* 【完整版】:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、IM 即时通讯、AI 大模型、IoT 物联网等功能
|
||||
* 【精简版】:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、IM 即时通讯、AI 大模型、IoT 物联网等功能
|
||||
|
||||
可参考 [《迁移文档》](https://cloud.iocoder.cn/migrate-module/) ,只需要 5-10 分钟,即可将【完整版】按需迁移到【精简版】
|
||||
|
||||
|
|
@ -105,7 +105,7 @@
|
|||
|
||||
团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师,可以提供全流程的外包服务。
|
||||
|
||||
项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 聊天、微信公众号、微信小程序等等。
|
||||
项目可以是商城、SCRM 系统、OA 系统、物流系统、ERP 系统、CMS 系统、HIS 系统、支付系统、IM 即时通讯、微信公众号、微信小程序等等。
|
||||
|
||||
## 🐼 内置功能
|
||||
|
||||
|
|
@ -115,7 +115,7 @@
|
|||
|
||||
* 通用模块(必选):系统功能、基础设施
|
||||
* 通用模块(可选):工作流程、支付系统、数据报表、会员中心
|
||||
* 业务系统(按需):ERP 系统、CRM 系统、MES 系统、商城系统、微信公众号、AI 大模型、IoT 物联网
|
||||
* 业务系统(按需):Mall 电子商城、OA 办公自动化、ERP 企业资源计划系统、WMS 仓库管理系统、CRM 客户关系管理、CMS 内容管理系统、MES 执行制造系统、AI 大模型平台、IoT 物联网系统、IM 即时通讯系统、Mobile 手机移动端、Report 数据大屏
|
||||
|
||||
> 友情提示:本项目基于 RuoYi-Vue 修改,**重构优化**后端的代码,**美化**前端的界面。
|
||||
>
|
||||
|
|
@ -273,6 +273,14 @@
|
|||
|
||||

|
||||
|
||||
### WMS 系统
|
||||
|
||||
演示地址:<https://cloud.iocoder.cn/wms-preview/>
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
### CRM 系统
|
||||
|
||||
演示地址:<https://cloud.iocoder.cn/crm-preview/>
|
||||
|
|
@ -303,6 +311,19 @@
|
|||
|
||||

|
||||
|
||||
### IM 即时通讯
|
||||
|
||||
演示地址(Cloud):<https://cloud.iocoder.cn/im-preview/>
|
||||
|
||||
演示地址(Vue3 + Element Plus):<http://dashboard-vue3.yudao.iocoder.cn>
|
||||
|
||||
|
||||

|
||||
|
||||
| 聊天界面 | 聊天管理 |
|
||||
| --- | --- |
|
||||
|  |  |
|
||||
|
||||
## 🐨 技术栈
|
||||
|
||||
### 微服务
|
||||
|
|
@ -321,6 +342,8 @@
|
|||
| `yudao-module-erp` | ERP 系统的 Module 模块 |
|
||||
| `yudao-module-crm` | CRM 系统的 Module 模块 |
|
||||
| `yudao-module-mes` | MES 系统的 Module 模块 |
|
||||
| `yudao-module-wms` | WMS 系统的 Module 模块 |
|
||||
| `yudao-module-im` | IM 即时通讯的 Module 模块 |
|
||||
| `yudao-module-ai` | AI 大模型的 Module 模块 |
|
||||
| `yudao-module-iot` | IoT 物联网的 Module 模块 |
|
||||
| `yudao-module-mp` | 微信公众号的 Module 模块 |
|
||||
|
|
|
|||
6
pom.xml
|
|
@ -26,6 +26,8 @@
|
|||
<module>yudao-module-crm</module>
|
||||
<module>yudao-module-iot</module>
|
||||
<module>yudao-module-mes</module>
|
||||
<module>yudao-module-wms</module>
|
||||
<module>yudao-module-im</module>
|
||||
<!-- 友情提示:基于 Spring AI 实现 LLM 大模型的接入,需要使用 JDK17 版本,详细可见 https://doc.iocoder.cn/ai/build/ -->
|
||||
<!-- <module>yudao-module-ai</module>-->
|
||||
</modules>
|
||||
|
|
@ -35,7 +37,7 @@
|
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>2026.04-jdk8-SNAPSHOT</revision>
|
||||
<revision>2026.05-jdk8-SNAPSHOT</revision>
|
||||
<!-- Maven 相关 -->
|
||||
<java.version>1.8</java.version>
|
||||
<maven.compiler.source>${java.version}</maven.compiler.source>
|
||||
|
|
@ -44,7 +46,7 @@
|
|||
<maven-compiler-plugin.version>3.14.0</maven-compiler-plugin.version>
|
||||
<flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
|
||||
<!-- maven-surefire-plugin 暂时无法通过 bom 的依赖读取(兼容老版本 IDEA 2024 及以前版本) -->
|
||||
<lombok.version>1.18.42</lombok.version>
|
||||
<lombok.version>1.18.46</lombok.version>
|
||||
<spring.boot.version>2.7.18</spring.boot.version>
|
||||
<mapstruct.version>1.6.3</mapstruct.version>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
|
|
|||
|
|
@ -1270,6 +1270,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
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 (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', '2025-09-06 00:02:21', '1', '2025-09-06 00:02:31', '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 (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '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 (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-12-16 19:25:51', '1', '2025-12-17 09:46:15', '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 (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '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 (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '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 (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', '2026-02-04 00:32:47', '1', '2026-02-04 00:32:47', '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 (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '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 (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
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 (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', '2025-09-06 00:02:21', '1', '2025-09-06 00:02:31', '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 (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '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 (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-12-16 19:25:51', '1', '2025-12-17 09:46:15', '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 (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '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 (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '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 (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', '2026-02-04 00:32:47', '1', '2026-02-04 00:32:47', '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 (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '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 (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
Target Server Version : 80200 (8.2.0)
|
||||
File Encoding : 65001
|
||||
|
||||
Date: 10/05/2026 10:16:10
|
||||
Date: 16/05/2026 06:24:32
|
||||
*/
|
||||
|
||||
SET NAMES utf8mb4;
|
||||
|
|
@ -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 = 23844 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 23883 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of infra_api_error_log
|
||||
|
|
@ -462,7 +462,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 = 3603 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1061096 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_dict_data
|
||||
|
|
@ -1095,6 +1095,8 @@ 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 (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', '2025-09-06 00:02:21', '1', '2025-09-06 00:02:31', 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 (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', 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 (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-12-16 19:25:51', '1', '2025-12-17 09:46:15', 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 (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), 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 (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), 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 (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', '2026-02-04 00:32:47', '1', '2026-02-04 00:32:47', 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 (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', 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 (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', b'0');
|
||||
|
|
@ -1437,6 +1439,25 @@ 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 (3600, 1, '未处理', '0', 'im_group_request_handle_result', 0, '', '', NULL, '', '2026-05-06 09:26:36', '', '2026-05-06 09:26:36', 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 (3601, 2, '同意', '1', 'im_group_request_handle_result', 0, '', '', NULL, '', '2026-05-06 09:26:36', '', '2026-05-06 09:26:36', 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 (3602, 3, '拒绝', '2', 'im_group_request_handle_result', 0, '', '', NULL, '', '2026-05-06 09:26:36', '', '2026-05-06 09:26:36', 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 (1061051, 1, '客户', '1', 'merchant_type', 0, 'primary', '', '客户', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', 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 (1061052, 2, '供应商', '2', 'merchant_type', 0, 'success', '', '供应商', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', 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 (1061053, 3, '客户/供应商', '3', 'merchant_type', 0, 'warning', '', '客户/供应商', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', 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 (1061061, 1, '入库单', '1', 'wms_order_type', 0, 'success', '', '', '1', '2026-05-10 17:51:46', '1', '2026-05-14 08:14:09', 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 (1061062, 2, '出库单', '2', 'wms_order_type', 0, 'danger', '', '', '1', '2026-05-10 17:51:46', '1', '2026-05-14 08:14:09', 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 (1061063, 3, '移库单', '3', 'wms_order_type', 0, 'primary', '', '', '1', '2026-05-10 17:51:46', '1', '2026-05-14 08:14:09', 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 (1061064, 4, '盘库单', '4', 'wms_order_type', 0, 'warning', '', '', '1', '2026-05-10 17:51:46', '1', '2026-05-14 08:14:09', 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 (1061071, 1, '草稿', '0', 'wms_order_status', 0, 'info', '', '草稿', '1', '2026-05-12 13:40:29', '1', '2026-05-12 13:40:29', 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 (1061072, 2, '已完成', '4', 'wms_order_status', 0, 'success', '', '已完成', '1', '2026-05-12 13:40:29', '1', '2026-05-12 13:40:29', 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 (1061073, 3, '已作废', '5', 'wms_order_status', 0, 'danger', '', '已作废', '1', '2026-05-12 13:40:29', '1', '2026-05-12 13:40:29', 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 (1061081, 1, '生产入库', '100', 'wms_receipt_order_type', 0, 'success', '', '', '1', '2026-05-11 11:21:49', '1', '2026-05-14 02:21: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 (1061082, 2, '采购入库', '101', 'wms_receipt_order_type', 0, 'primary', '', '', '1', '2026-05-11 11:21:49', '1', '2026-05-14 02:21: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 (1061083, 3, '退货入库', '102', 'wms_receipt_order_type', 0, 'warning', '', '', '1', '2026-05-11 11:21:49', '1', '2026-05-14 02:21: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 (1061084, 4, '归还入库', '103', 'wms_receipt_order_type', 0, 'info', '', '', '1', '2026-05-13 16:02:33', '1', '2026-05-14 02:21: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 (1061091, 1, '退货出库', '200', 'wms_shipment_order_type', 0, 'warning', '', '退货出库', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48:35', 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 (1061092, 2, '销售出库', '201', 'wms_shipment_order_type', 0, 'primary', '', '销售出库', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48:35', 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 (1061093, 3, '生产出库', '202', 'wms_shipment_order_type', 0, 'success', '', '生产出库', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48:35', 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 (1061094, 1, 'è¯éŸ³', '1', 'im_rtc_call_media_type', 0, '', '', 'è¯éŸ³é€šè¯', 'admin', '2026-05-14 13:48:31', 'admin', '2026-05-14 13:48:31', 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 (1061095, 2, '视频', '2', 'im_rtc_call_media_type', 0, '', '', '视频通è¯', 'admin', '2026-05-14 13:48:31', 'admin', '2026-05-14 13:48:31', b'0');
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
|
@ -1456,7 +1477,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 = 2211 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 1061092 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_dict_type
|
||||
|
|
@ -1658,6 +1679,12 @@ 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 (2208, 'IM 好友申请处理结果', 'im_friend_request_handle_result', 0, NULL, '1', '2026-05-04 02:43:41', '1', '2026-05-04 02:43:41', b'0', NULL);
|
||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2209, 'IM 加群来源', 'im_group_add_source', 0, NULL, '', '2026-05-06 09:26:36', '', '2026-05-06 09:26:36', b'0', NULL);
|
||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2210, 'IM 加群申请处理结果', 'im_group_request_handle_result', 0, NULL, '', '2026-05-06 09:26:36', '', '2026-05-06 09:26:36', b'0', NULL);
|
||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061050, '往来企业类型', 'merchant_type', 0, 'WMS 往来企业类型', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', b'0', NULL);
|
||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061060, 'WMS 单据类型', 'wms_order_type', 0, 'WMS 单据类型', '1', '2026-05-10 17:51:46', '1', '2026-05-14 08:14:09', b'0', NULL);
|
||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061070, 'WMS 单据状态', 'wms_order_status', 0, 'WMS 单据状态', '1', '2026-05-12 13:40:29', '1', '2026-05-12 13:40:29', b'0', NULL);
|
||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061080, '入库单类型', 'wms_receipt_order_type', 0, 'WMS 入库单类型', '1', '2026-05-11 11:21:49', '1', '2026-05-12 13:40:29', b'0', NULL);
|
||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061090, '出库单类型', 'wms_shipment_order_type', 0, 'WMS 出库单类型', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48:35', b'0', NULL);
|
||||
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061091, 'IM 通è¯åª’体类型', 'im_rtc_call_media_type', 0, NULL, 'admin', '2026-05-14 13:48:31', 'admin', '2026-05-14 13:48:31', b'0', NULL);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
|
@ -1683,7 +1710,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 = 4550 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 4557 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_login_log
|
||||
|
|
@ -3144,22 +3171,74 @@ 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 (5984, '上工下工', 'mes:pro-workrecord:clock', 3, 3, 5981, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-04-05 14:08:44', '1', '2026-04-05 14:08:44', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5985, 'MES 首页', 'mes:home:query', 2, 0, 5100, '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-04-06 01:20: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 (6400, 'WMS 系统', '', 1, 310, 0, '/wms', 'ep:box', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6401, '基础数据', '', 1, 1, 6400, 'md', 'ep:files', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-10 00:54: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 (6401, '基础数据', '', 1, 6, 6400, 'md', 'ep:files', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-12 17:48: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 (6402, '仓库管理', '', 2, 1, 6401, '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-05-10 00:54: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 (6403, '仓库查询', 'wms:warehouse:query', 3, 1, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6404, '仓库创建', 'wms:warehouse:create', 3, 2, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6405, '仓库更新', 'wms:warehouse:update', 3, 3, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6406, '仓库删除', 'wms:warehouse:delete', 3, 4, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6407, '库区查询', 'wms:warehouse-area:query', 3, 5, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6408, '库区创建', 'wms:warehouse-area:create', 3, 6, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6409, '库区更新', 'wms:warehouse-area:update', 3, 7, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6410, '库区删除', 'wms:warehouse-area:delete', 3, 8, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11: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 (6411, '商品品牌', '', 2, 3, 6401, 'item/brand', 'ep:price-tag', 'wms/md/item/brand/index', 'WmsItemBrand', 0, b'1', b'1', b'1', '1', '2026-05-10 01:43:12', '1', '2026-05-10 02:12:51', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6412, '品牌查询', 'wms:item-brand:query', 3, 1, 6411, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-10 01:43:12', '1', '2026-05-10 01:43:12', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6413, '品牌创建', 'wms:item-brand:create', 3, 2, 6411, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-10 01:43:12', '1', '2026-05-10 01:43:12', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6414, '品牌更新', 'wms:item-brand:update', 3, 3, 6411, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-10 01:43:12', '1', '2026-05-10 01:43:12', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6415, '品牌删除', 'wms:item-brand:delete', 3, 4, 6411, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-10 01:43:12', '1', '2026-05-10 01:43:12', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6416, '品牌导出', 'wms:item-brand:export', 3, 5, 6411, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-10 01:43:12', '1', '2026-05-10 01:43:12', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6417, '仓库导出', 'wms:warehouse:export', 3, 5, 6402, '', '', '', '', 0, b'1', b'1', b'1', '1', '2026-05-10 02:42:47', '1', '2026-05-10 02:42:47', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6419, '商品分类', '', 2, 2, 6401, 'item/category', 'ep:folder', 'wms/md/item/category/index', 'WmsItemCategory', 0, b'1', b'1', b'1', '1', '2026-05-10 07:14:18', '1', '2026-05-10 07:14: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 (6420, '分类查询', 'wms:item-category:query', 3, 1, 6419, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 07:14:18', '1', '2026-05-10 07:14: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 (6421, '分类创建', 'wms:item-category:create', 3, 2, 6419, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 07:14:18', '1', '2026-05-10 07:14: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 (6422, '分类更新', 'wms:item-category:update', 3, 3, 6419, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 07:14:18', '1', '2026-05-10 07:14: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 (6423, '分类删除', 'wms:item-category:delete', 3, 4, 6419, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 07:14:18', '1', '2026-05-10 07:14: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 (6424, '商品管理', '', 2, 4, 6401, 'item', 'ep:goods', 'wms/md/item/index', 'WmsItem', 0, b'1', b'1', b'1', '1', '2026-05-10 09:15:34', '1', '2026-05-10 09:15: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 (6425, '商品查询', 'wms:item:query', 3, 1, 6424, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 09:15:34', '1', '2026-05-10 09:15: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 (6426, '商品创建', 'wms:item:create', 3, 2, 6424, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 09:15:34', '1', '2026-05-10 09:15: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 (6427, '商品更新', 'wms:item:update', 3, 3, 6424, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 09:15:34', '1', '2026-05-10 09:15: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 (6428, '商品删除', 'wms:item:delete', 3, 4, 6424, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 09:15:34', '1', '2026-05-10 09:15: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 (6429, '商品导出', 'wms:item:export', 3, 5, 6424, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 09:15:34', '1', '2026-05-10 09:15: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 (6430, '往来企业', '', 2, 5, 6401, 'merchant', 'ep:office-building', 'wms/md/merchant/index', 'WmsMerchant', 0, b'1', b'1', b'1', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:48:07', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6431, '往来企业查询', 'wms:merchant:query', 3, 1, 6430, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6432, '往来企业创建', 'wms:merchant:create', 3, 2, 6430, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6433, '往来企业更新', 'wms:merchant:update', 3, 3, 6430, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6434, '往来企业删除', 'wms:merchant:delete', 3, 4, 6430, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6435, '往来企业导出', 'wms:merchant:export', 3, 5, 6430, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 15:26:09', '1', '2026-05-10 15:26:09', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6440, '库存管理', '', 1, 5, 6400, 'inventory', 'ep:box', '', '', 0, b'1', b'1', b'1', '1', '2026-05-10 17:51:46', '1', '2026-05-13 01:23: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 (6441, '库存统计', '', 2, 1, 6440, 'index', 'ep:data-board', 'wms/inventory/index/index', 'WmsInventory', 0, b'1', b'1', b'1', '1', '2026-05-10 17:51:46', '1', '2026-05-11 02:08:28', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6442, '库存统计查询', 'wms:inventory:query', 3, 1, 6441, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 17:51:46', '1', '2026-05-11 00:30: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 (6445, '库存流水', '', 2, 2, 6440, 'history', 'ep:document', 'wms/inventory/history/index', 'WmsInventoryHistory', 0, b'1', b'1', b'1', '1', '2026-05-10 17:51:46', '1', '2026-05-14 07:59: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 (6446, '库存流水查询', 'wms:inventory-history:query', 3, 1, 6445, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-10 17:51:46', '1', '2026-05-11 00:29: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 (6451, '入库管理', '', 2, 1, 6400, 'receipt', 'ep:download', 'wms/order/receipt/index', 'WmsReceiptOrder', 0, b'1', b'1', b'1', '1', '2026-05-11 11:58:58', '1', '2026-05-11 16:58:02', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6452, '入库单查询', 'wms:receipt-order:query', 3, 1, 6451, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-11 11:58:58', '1', '2026-05-11 16:58:02', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6453, '入库单创建', 'wms:receipt-order:create', 3, 2, 6451, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-11 11:58:58', '1', '2026-05-11 16:58:02', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6454, '入库单更新', 'wms:receipt-order:update', 3, 3, 6451, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-11 11:58:58', '1', '2026-05-11 16:58:02', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6455, '入库单删除', 'wms:receipt-order:delete', 3, 4, 6451, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-11 11:58:58', '1', '2026-05-11 16:58:02', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6456, '入库单完成入库', 'wms:receipt-order:complete', 3, 5, 6451, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-11 11:58:58', '1', '2026-05-11 16:58:02', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6457, '入库单作废', 'wms:receipt-order:cancel', 3, 6, 6451, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-11 11:58:58', '1', '2026-05-12 16:27: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 (6458, '入库单导出', 'wms:receipt-order:export', 3, 7, 6451, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-11 16:58:02', '1', '2026-05-12 16:27: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 (6461, '出库管理', '', 2, 2, 6400, 'shipment', 'ep:upload', 'wms/order/shipment/index', 'WmsShipmentOrder', 0, b'1', b'1', b'1', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48: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 (6462, '出库单查询', 'wms:shipment-order:query', 3, 1, 6461, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48: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 (6463, '出库单创建', 'wms:shipment-order:create', 3, 2, 6461, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48: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 (6464, '出库单更新', 'wms:shipment-order:update', 3, 3, 6461, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48: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 (6465, '出库单删除', 'wms:shipment-order:delete', 3, 4, 6461, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48: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 (6466, '出库单完成出库', 'wms:shipment-order:complete', 3, 5, 6461, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48: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 (6467, '出库单作废', 'wms:shipment-order:cancel', 3, 6, 6461, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48: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 (6468, '出库单导出', 'wms:shipment-order:export', 3, 7, 6461, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-12 17:48:35', '1', '2026-05-12 17:48: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 (6470, '移库管理', '', 2, 3, 6400, 'movement', 'ep:sort', 'wms/order/movement/index', 'WmsMovementOrder', 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6471, '移库单查询', 'wms:movement-order:query', 3, 1, 6470, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6472, '移库单创建', 'wms:movement-order:create', 3, 2, 6470, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6473, '移库单更新', 'wms:movement-order:update', 3, 3, 6470, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6474, '移库单删除', 'wms:movement-order:delete', 3, 4, 6470, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6475, '移库单完成移库', 'wms:movement-order:complete', 3, 5, 6470, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6476, '移库单作废', 'wms:movement-order:cancel', 3, 6, 6470, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6477, '移库单导出', 'wms:movement-order:export', 3, 7, 6470, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6480, '盘库管理', '', 2, 4, 6400, 'check', 'ep:circle-check-filled', 'wms/order/check/index', 'WmsCheckOrder', 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6481, '盘库单查询', 'wms:check-order:query', 3, 1, 6480, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6482, '盘库单创建', 'wms:check-order:create', 3, 2, 6480, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6483, '盘库单更新', 'wms:check-order:update', 3, 3, 6480, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6484, '盘库单删除', 'wms:check-order:delete', 3, 4, 6480, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6485, '盘库单完成盘库', 'wms:check-order:complete', 3, 5, 6480, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6486, '盘库单作废', 'wms:check-order:cancel', 3, 6, 6480, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6487, '盘库单导出', 'wms:check-order:export', 3, 7, 6480, '', '#', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-13 01:23:11', '1', '2026-05-14 02:21:20', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6490, 'WMS 首页', 'wms:home:query', 2, 0, 6400, 'home', 'ep:home-filled', 'wms/home/index', 'WmsHome', 0, b'1', b'1', b'1', '1', '2026-05-14 09:34:27', '1', '2026-05-14 10:05: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 (6500, '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-05-09 16: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 (6510, '数据统计', 'im:manager:statistics:query', 2, 10, 6500, '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-04-30 19:35:54', b'0');
|
||||
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6515, '好友申请', 'im:manager:friend-request:query', 2, 20, 6600, '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-05-07 00:40:35', b'0');
|
||||
|
|
@ -3315,7 +3394,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 = 53612 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 54457 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 访问令牌';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_oauth2_access_token
|
||||
|
|
@ -3441,7 +3520,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 = 2583 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 2590 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 刷新令牌';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_oauth2_refresh_token
|
||||
|
|
@ -3566,7 +3645,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 = 6380 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
|
||||
) ENGINE = InnoDB AUTO_INCREMENT = 6381 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
|
||||
|
||||
-- ----------------------------
|
||||
-- Records of system_role_menu
|
||||
|
|
@ -4465,10 +4544,7 @@ 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 (6369, 2, 6404, '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11:01', b'0', 1);
|
||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6370, 2, 6405, '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11:01', b'0', 1);
|
||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6371, 2, 6406, '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11:01', b'0', 1);
|
||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6372, 2, 6407, '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11:01', b'0', 1);
|
||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6373, 2, 6408, '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11:01', b'0', 1);
|
||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6374, 2, 6409, '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11:01', b'0', 1);
|
||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6375, 2, 6410, '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11:01', b'0', 1);
|
||||
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6380, 2, 6490, '1', '2026-05-14 09:36:12', '1', '2026-05-14 09:36:12', b'0', 1);
|
||||
COMMIT;
|
||||
|
||||
-- ----------------------------
|
||||
|
|
@ -4884,7 +4960,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/user/avatar/20251220/blob_1766215463801.jpg', 0, '0:0:0:0:0:0:0:1', '2026-05-09 23:54:42', 'admin', '2021-01-05 17:03:47', NULL, '2026-05-09 23:54:42', 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/user/avatar/20251220/blob_1766215463801.jpg', 0, '0:0:0:0:0:0:0:1', '2026-05-15 22:24:39', 'admin', '2021-01-05 17:03:47', NULL, '2026-05-15 22:24:39', 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, '0:0:0:0:0:0:0:1', '2026-04-19 17:40:55', '', '2021-01-07 09:07:17', NULL, '2026-04-19 17:40:55', 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$BrwaYn303hjA/6TnXqdGoOLhyHOAA0bVrAFu6.1dJKycqKUnIoRz2', '测试号', NULL, 107, '[1,2]', '111@qq.com', '15601691200', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2026-05-09 09:57:13', '', '2021-01-21 02:13:53', NULL, '2026-05-09 09:57:13', b'0', 1);
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
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 (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', '2025-09-06 00:02:21', '1', '2025-09-06 00:02:31', '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 (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '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 (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-12-16 19:25:51', '1', '2025-12-17 09:46:15', '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 (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '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 (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '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 (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', '2026-02-04 00:32:47', '1', '2026-02-04 00:32:47', '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 (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '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 (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
|
|
|
|||
|
|
@ -1323,6 +1323,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
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 (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', to_date('2025-09-06 00:02:21', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-09-06 00:02:31', 'SYYYY-MM-DD HH24:MI:SS'), '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 (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', to_date('2023-11-04 13:05:38', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2023-11-04 13:07:16', 'SYYYY-MM-DD HH24:MI:SS'), '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 (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', to_date('2025-12-16 19:25:51', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2025-12-17 09:46:15', 'SYYYY-MM-DD HH24:MI:SS'), '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 (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', SYSDATE, '1', SYSDATE, '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 (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', SYSDATE, '1', SYSDATE, '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 (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', to_date('2026-02-04 00:32:47', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-02-04 00:32:47', 'SYYYY-MM-DD HH24:MI:SS'), '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 (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', to_date('2026-02-04 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-02-04 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '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 (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', to_date('2026-02-04 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-02-04 00:32:55', 'SYYYY-MM-DD HH24:MI:SS'), '0');
|
||||
|
|
|
|||
|
|
@ -1371,6 +1371,8 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
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 (3034, 1, 'ttt', 'tt', 'iot_ota_task_record_status', 0, 'success', '', NULL, '1', '2025-09-06 00:02:21', '1', '2025-09-06 00:02:31', '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 (3035, 40, '支付宝小程序', '40', 'system_social_type', 0, '', '', '', '1', '2023-11-04 13:05:38', '1', '2023-11-04 13:07:16', '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 (3036, 60, 'Admin Uniapp 移动端', '60', 'infra_codegen_front_type', 0, '', '', NULL, '1', '2025-12-16 19:25:51', '1', '2025-12-17 09:46:15', '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 (3037, 42, 'Vben5.0 Antdv Next Schema 模版', '42', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '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 (3038, 43, 'Vben5.0 Antdv Next 标准模版', '43', 'infra_codegen_front_type', 0, '', '', '', '1', NOW(), '1', NOW(), '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 (3040, 1, 'UDP', 'udp', 'iot_protocol_type', 0, '', '', 'UDP 协议', '1', '2026-02-04 00:32:47', '1', '2026-02-04 00:32:47', '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 (3041, 2, 'WebSocket', 'websocket', 'iot_protocol_type', 0, '', '', 'WebSocket 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '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 (3042, 3, 'HTTP', 'http', 'iot_protocol_type', 0, '', '', 'HTTP 协议', '1', '2026-02-04 00:32:55', '1', '2026-02-04 00:32:55', '0');
|
||||
|
|
|
|||
|
|
@ -3367,6 +3367,10 @@ INSERT INTO system_dict_data (id, sort, label, value, dict_type, status, color_t
|
|||
GO
|
||||
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 (3036, 60, N'Admin Uniapp 移动端', N'60', N'infra_codegen_front_type', 0, N'', N'', NULL, N'1', N'2025-12-16 19:25:51', N'1', N'2025-12-17 09:46:15', N'0')
|
||||
GO
|
||||
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 (3037, 42, N'Vben5.0 Antdv Next Schema 模版', N'42', N'infra_codegen_front_type', 0, N'', N'', N'', N'1', N'2026-05-16 00:00:00', N'1', N'2026-05-16 00:00:00', N'0')
|
||||
GO
|
||||
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 (3038, 43, N'Vben5.0 Antdv Next 标准模版', N'43', N'infra_codegen_front_type', 0, N'', N'', N'', N'1', N'2026-05-16 00:00:00', N'1', N'2026-05-16 00:00:00', N'0')
|
||||
GO
|
||||
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 (3040, 1, N'UDP', N'udp', N'iot_protocol_type', 0, N'', N'', N'UDP 协议', N'1', N'2026-02-04 00:32:47', N'1', N'2026-02-04 00:32:47', N'0')
|
||||
GO
|
||||
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 (3041, 2, N'WebSocket', N'websocket', N'iot_protocol_type', 0, N'', N'', N'WebSocket 协议', N'1', N'2026-02-04 00:32:55', N'1', N'2026-02-04 00:32:55', N'0')
|
||||
|
|
|
|||
|
|
@ -77,6 +77,9 @@ def load_and_clean(sql_file: str) -> str:
|
|||
|
||||
|
||||
class Convertor(ABC):
|
||||
# 不同数据库的关键字不完全一致;子类按需声明需要转义的列名。
|
||||
reserved_column_names = set()
|
||||
|
||||
def __init__(self, src: str, db_type) -> None:
|
||||
self.src = src
|
||||
self.db_type = db_type
|
||||
|
|
@ -179,6 +182,31 @@ class Convertor(ABC):
|
|||
"""
|
||||
return ""
|
||||
|
||||
def escape_column_name(self, name: str) -> str:
|
||||
"""转义目标库保留字列名,例如 Oracle / Kingbase 的 level。"""
|
||||
|
||||
column_name = name.lower()
|
||||
if column_name in self.reserved_column_names:
|
||||
return f'"{column_name}"'
|
||||
return column_name
|
||||
|
||||
def escape_insert_columns(self, insert_script: str) -> str:
|
||||
"""INSERT 显式列清单需要和 CREATE / COMMENT 使用同一套列名转义。"""
|
||||
|
||||
match = re.match(
|
||||
r"(INSERT INTO\s+\S+\s*\()([^)]+)(\)\s+VALUES\s+[\s\S]*)",
|
||||
insert_script,
|
||||
flags=re.IGNORECASE,
|
||||
)
|
||||
if not match:
|
||||
return insert_script
|
||||
|
||||
columns = [
|
||||
self.escape_column_name(column.strip())
|
||||
for column in match.group(2).split(",")
|
||||
]
|
||||
return f"{match.group(1)}{', '.join(columns)}{match.group(3)}"
|
||||
|
||||
@staticmethod
|
||||
def inserts(table_name: str, script_content: str) -> Generator:
|
||||
PREFIX = f"INSERT INTO `{table_name}`"
|
||||
|
|
@ -204,18 +232,55 @@ class Convertor(ABC):
|
|||
Generator[str]: create index 语句
|
||||
"""
|
||||
|
||||
def generate_columns(columns):
|
||||
keys = [
|
||||
f"{col['name'].lower()}{' ' + col['order'].lower() if col['order'] != 'ASC' else ''}"
|
||||
for col in columns[0]
|
||||
]
|
||||
return ", ".join(keys)
|
||||
|
||||
for no, index in enumerate(ddl["index"], 1):
|
||||
columns = generate_columns(index["columns"])
|
||||
for no, index in enumerate(ddl.get("index", []), 1):
|
||||
columns = ", ".join(Convertor.index_columns(index.get("columns", [])))
|
||||
if not columns:
|
||||
continue
|
||||
table_name = ddl["table_name"].lower()
|
||||
yield f"CREATE INDEX idx_{table_name}_{no:02d} ON {table_name} ({columns})"
|
||||
|
||||
@staticmethod
|
||||
def index_columns(columns) -> list:
|
||||
"""兼容 simple-ddl-parser 不同版本的索引列结构。"""
|
||||
|
||||
keys = []
|
||||
|
||||
def append(name, order="ASC"):
|
||||
if not name:
|
||||
return
|
||||
column_name = str(name).strip("`").lower()
|
||||
column_order = str(order or "ASC").upper()
|
||||
if column_order == "DESC":
|
||||
keys.append(f"{column_name} desc")
|
||||
else:
|
||||
keys.append(column_name)
|
||||
|
||||
def visit(value):
|
||||
# 普通索引常见结构:[[{'name': 'user_id', 'order': 'ASC'}]]
|
||||
if isinstance(value, (list, tuple)):
|
||||
for item in value:
|
||||
visit(item)
|
||||
return
|
||||
if isinstance(value, dict):
|
||||
name = value.get("name")
|
||||
if isinstance(name, (dict, list, tuple)):
|
||||
visit(name)
|
||||
return
|
||||
append(name, value.get("order", "ASC"))
|
||||
return
|
||||
# 唯一索引在部分版本中会被解析成 ['mobile', 'ASC', 'tenant_id', 'ASC']。
|
||||
if isinstance(value, str):
|
||||
token = value.strip("`")
|
||||
order = token.upper()
|
||||
if order in ("ASC", "DESC"):
|
||||
if order == "DESC" and keys and not keys[-1].endswith(" desc"):
|
||||
keys[-1] = f"{keys[-1]} desc"
|
||||
return
|
||||
append(token)
|
||||
|
||||
visit(columns)
|
||||
return keys
|
||||
|
||||
@staticmethod
|
||||
def unique_index(ddl: Dict) -> Generator:
|
||||
if "constraints" in ddl and "uniques" in ddl["constraints"]:
|
||||
|
|
@ -223,7 +288,9 @@ class Convertor(ABC):
|
|||
for uk in uk_list:
|
||||
table_name = ddl["table_name"]
|
||||
uk_name = uk["constraint_name"]
|
||||
uk_columns = uk["columns"]
|
||||
uk_columns = Convertor.index_columns(uk["columns"])
|
||||
if not uk_columns:
|
||||
continue
|
||||
yield table_name, uk_name, uk_columns
|
||||
|
||||
@staticmethod
|
||||
|
|
@ -381,7 +448,7 @@ class PostgreSQLConvertor(Convertor):
|
|||
)
|
||||
nullable = "NULL" if col["nullable"] else "NOT NULL"
|
||||
default = f"DEFAULT {col['default']}" if col["default"] is not None else ""
|
||||
return f"{name} {full_type} {nullable} {default}"
|
||||
return f"{self.escape_column_name(name)} {full_type} {nullable} {default}"
|
||||
|
||||
table_name = ddl["table_name"].lower()
|
||||
columns = [f"{_generate_column(col).strip()}" for col in ddl["columns"]]
|
||||
|
|
@ -406,7 +473,7 @@ CREATE TABLE {table_name} (
|
|||
for column in table_ddl["columns"]:
|
||||
table_comment = column["comment"]
|
||||
script += (
|
||||
f"COMMENT ON COLUMN {table_ddl['table_name']}.{column['name']} IS '{table_comment}';"
|
||||
f"COMMENT ON COLUMN {table_ddl['table_name']}.{self.escape_column_name(column['name'])} IS '{table_comment}';"
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
|
|
@ -435,6 +502,7 @@ CREATE TABLE {table_name} (
|
|||
"""生成 insert 语句,以及根据最后的 insert id+1 生成 Sequence"""
|
||||
|
||||
inserts = list(Convertor.inserts(table_name, self.content))
|
||||
inserts = [self.escape_insert_columns(s) for s in inserts]
|
||||
# 转换 MySQL 字符串转义为 PostgreSQL 格式:\\ -> \,\' -> ''
|
||||
inserts = [re.sub(r"\\\\|\\'", lambda m: "\\" if m.group() == "\\\\" else "''", s) for s in inserts]
|
||||
## 生成 insert 脚本
|
||||
|
|
@ -482,6 +550,8 @@ INSERT INTO dual VALUES (1);
|
|||
|
||||
|
||||
class OracleConvertor(Convertor):
|
||||
reserved_column_names = {"level", "size"}
|
||||
|
||||
def __init__(self, src):
|
||||
super().__init__(src, "Oracle")
|
||||
|
||||
|
|
@ -526,10 +596,8 @@ class OracleConvertor(Convertor):
|
|||
# Oracle的 INSERT '' 不能通过NOT NULL校验,因此对文字类型字段覆写为 NULL
|
||||
nullable = "NULL" if type in ("varchar", "text", "longtext") else nullable
|
||||
default = f"DEFAULT {col['default']}" if col["default"] is not None else ""
|
||||
# Oracle 中 size 不能作为字段名
|
||||
field_name = '"size"' if name == "size" else name
|
||||
# Oracle DEFAULT 定义在 NULLABLE 之前
|
||||
return f"{field_name} {full_type} {default} {nullable}"
|
||||
return f"{self.escape_column_name(name)} {full_type} {default} {nullable}"
|
||||
|
||||
table_name = ddl["table_name"].lower()
|
||||
columns = [f"{generate_column(col).strip()}" for col in ddl["columns"]]
|
||||
|
|
@ -554,7 +622,7 @@ CREATE TABLE {table_name} (
|
|||
for column in table_ddl["columns"]:
|
||||
table_comment = column["comment"]
|
||||
script += (
|
||||
f"COMMENT ON COLUMN {table_ddl['table_name']}.{column['name']} IS '{table_comment}';"
|
||||
f"COMMENT ON COLUMN {table_ddl['table_name']}.{self.escape_column_name(column['name'])} IS '{table_comment}';"
|
||||
+ "\n"
|
||||
)
|
||||
|
||||
|
|
@ -586,6 +654,7 @@ CREATE TABLE {table_name} (
|
|||
"""拷贝 INSERT 语句"""
|
||||
inserts = []
|
||||
for insert_script in Convertor.inserts(table_name, self.content):
|
||||
insert_script = self.escape_insert_columns(insert_script)
|
||||
# 对日期数据添加 TO_DATE 转换
|
||||
insert_script = re.sub(
|
||||
r"('\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}')",
|
||||
|
|
@ -907,6 +976,8 @@ SET IDENTITY_INSERT {table_name.lower()} OFF;
|
|||
|
||||
|
||||
class KingbaseConvertor(PostgreSQLConvertor):
|
||||
reserved_column_names = {"level"}
|
||||
|
||||
def __init__(self, src):
|
||||
super().__init__(src)
|
||||
self.db_type = "Kingbase"
|
||||
|
|
@ -925,7 +996,7 @@ class KingbaseConvertor(PostgreSQLConvertor):
|
|||
if full_type == "text":
|
||||
nullable = "NULL"
|
||||
default = f"DEFAULT {col['default']}" if col["default"] is not None else ""
|
||||
return f"{name} {full_type} {nullable} {default}"
|
||||
return f"{self.escape_column_name(name)} {full_type} {nullable} {default}"
|
||||
|
||||
table_name = ddl["table_name"].lower()
|
||||
columns = [f"{_generate_column(col).strip()}" for col in ddl["columns"]]
|
||||
|
|
@ -945,6 +1016,8 @@ CREATE TABLE {table_name} (
|
|||
|
||||
|
||||
class OpengaussConvertor(KingbaseConvertor):
|
||||
reserved_column_names = set()
|
||||
|
||||
def __init__(self, src):
|
||||
super().__init__(src)
|
||||
self.db_type = "OpenGauss"
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@
|
|||
<url>https://github.com/YunaiV/ruoyi-vue-pro</url>
|
||||
|
||||
<properties>
|
||||
<revision>2026.04-jdk8-SNAPSHOT</revision>
|
||||
<revision>2026.05-jdk8-SNAPSHOT</revision>
|
||||
<flatten-maven-plugin.version>1.6.0</flatten-maven-plugin.version>
|
||||
<!-- 统一依赖管理 -->
|
||||
<spring.framework.version>5.3.39</spring.framework.version>
|
||||
|
|
@ -33,7 +33,7 @@
|
|||
<mybatis-plus-join.version>1.5.7</mybatis-plus-join.version>
|
||||
<dynamic-datasource.version>4.5.0</dynamic-datasource.version>
|
||||
<easy-trans.version>3.0.6</easy-trans.version>
|
||||
<redisson.version>4.3.1</redisson.version>
|
||||
<redisson.version>4.4.0</redisson.version>
|
||||
<dm8.jdbc.version>8.1.3.140</dm8.jdbc.version>
|
||||
<kingbase.jdbc.version>9.0.1.jre7</kingbase.jdbc.version>
|
||||
<opengauss.jdbc.version>7.0.0-RC3-og</opengauss.jdbc.version>
|
||||
|
|
@ -59,6 +59,8 @@
|
|||
<!-- 工具类相关 -->
|
||||
<anji-plus-captcha.version>1.4.0</anji-plus-captcha.version>
|
||||
<jsoup.version>1.22.2</jsoup.version>
|
||||
<sensitive-word.version>0.29.5</sensitive-word.version>
|
||||
<pinyin4j.version>2.5.1</pinyin4j.version>
|
||||
<lombok.version>1.18.46</lombok.version>
|
||||
<mapstruct.version>1.6.3</mapstruct.version>
|
||||
<hutool-5.version>5.8.44</hutool-5.version>
|
||||
|
|
@ -74,7 +76,7 @@
|
|||
<ip2region.version>2.7.0</ip2region.version>
|
||||
<bizlog-sdk.version>3.0.6</bizlog-sdk.version>
|
||||
<reflections.version>0.10.2</reflections.version>
|
||||
<netty.version>4.2.12.Final</netty.version>
|
||||
<netty.version>4.2.14.Final</netty.version>
|
||||
<mqtt.version>1.2.5</mqtt.version>
|
||||
<vertx.version>4.5.26</vertx.version>
|
||||
<okhttp.version>4.12.0</okhttp.version>
|
||||
|
|
@ -86,10 +88,11 @@
|
|||
<awssdk.version>2.44.0</awssdk.version>
|
||||
<justauth.version>1.16.7</justauth.version>
|
||||
<justauth-starter.version>1.4.0</justauth-starter.version>
|
||||
<jimureport.version>2.3.2</jimureport.version>
|
||||
<jimureport.version>2.3.4</jimureport.version>
|
||||
<jimubi.version>2.3.2</jimubi.version>
|
||||
<weixin-java.version>4.8.2-20260501.180637</weixin-java.version>
|
||||
<alipay-sdk-java.version>4.40.771.ALL</alipay-sdk-java.version>
|
||||
<bouncycastle.version>1.80</bouncycastle.version>
|
||||
<alipay-sdk-java.version>4.40.806.ALL</alipay-sdk-java.version>
|
||||
<!-- 专属于 JDK8 安全漏洞升级 -->
|
||||
<logback.version>1.2.13</logback.version> <!-- 无法使用 1.3.X 版本,启动会报错 -->
|
||||
</properties>
|
||||
|
|
@ -624,6 +627,18 @@
|
|||
<version>${jsoup.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>sensitive-word</artifactId> <!-- 敏感词检测:trie 树高效匹配 -->
|
||||
<version>${sensitive-word.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.belerweb</groupId>
|
||||
<artifactId>pinyin4j</artifactId> <!-- 汉字转拼音:作为 hutool PinyinUtil 的底层引擎 -->
|
||||
<version>${pinyin4j.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.reflections</groupId>
|
||||
<artifactId>reflections</artifactId>
|
||||
|
|
@ -735,6 +750,24 @@
|
|||
</exclusions>
|
||||
</dependency>
|
||||
|
||||
<!-- 锁定 weixin-java 传递依赖,避免 Maven 版本范围自动升级到 1.80.2 后 Fat Jar 启动失败。
|
||||
反馈:https://t.zsxq.com/pCVBo -->
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcutil-jdk18on</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcpkix-jdk18on</artifactId>
|
||||
<version>${bouncycastle.version}</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.binarywang</groupId>
|
||||
<artifactId>weixin-java-pay</artifactId>
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,只有工具类需要使用到 -->
|
||||
</dependency>
|
||||
|
||||
|
|
@ -125,8 +125,8 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,主要是 PageParam 使用到 -->
|
||||
</dependency>
|
||||
|
||||
|
|
|
|||
|
|
@ -124,6 +124,22 @@ public class CollectionUtils {
|
|||
return from.stream().filter(filter).map(func).filter(Objects::nonNull).collect(Collectors.toSet());
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertLinkedSet(Collection<T> from, Function<T, U> func) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new LinkedHashSet<>();
|
||||
}
|
||||
return from.stream().map(func).filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
}
|
||||
|
||||
public static <T, U> Set<U> convertLinkedSet(Collection<T> from, Function<T, U> func, Predicate<T> filter) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new LinkedHashSet<>();
|
||||
}
|
||||
return from.stream().filter(filter).map(func).filter(Objects::nonNull)
|
||||
.collect(Collectors.toCollection(LinkedHashSet::new));
|
||||
}
|
||||
|
||||
public static <T, K> Map<K, T> convertMapByFilter(Collection<T> from, Predicate<T> filter, Function<T, K> keyFunc) {
|
||||
if (CollUtil.isEmpty(from)) {
|
||||
return new HashMap<>();
|
||||
|
|
@ -372,4 +388,14 @@ public class CollectionUtils {
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把单元素 head 与集合 tail 合并成新 List(head 在前,tail 顺序保留)
|
||||
*/
|
||||
public static <T> List<T> of(T head, Collection<T> tail) {
|
||||
List<T> list = new ArrayList<>();
|
||||
list.add(head);
|
||||
CollUtil.addAll(list, tail);
|
||||
return list;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -236,6 +236,18 @@ public class LocalDateTimeUtils {
|
|||
return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取最近 N 天的 0 点时刻序列(升序,含今天)
|
||||
*/
|
||||
public static List<LocalDateTime> getLatestDays(int days) {
|
||||
LocalDateTime today = getToday();
|
||||
List<LocalDateTime> dates = new ArrayList<>(days);
|
||||
for (int i = days - 1; i >= 0; i--) {
|
||||
dates.add(today.minusDays(i));
|
||||
}
|
||||
return dates;
|
||||
}
|
||||
|
||||
public static List<LocalDateTime[]> getDateRangeList(LocalDateTime startTime,
|
||||
LocalDateTime endTime,
|
||||
Integer interval) {
|
||||
|
|
@ -302,6 +314,21 @@ public class LocalDateTimeUtils {
|
|||
return timeRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取从开始日期起的日期列表
|
||||
*
|
||||
* @param startDate 开始日期
|
||||
* @param days 天数
|
||||
* @return 日期列表,包含开始日期
|
||||
*/
|
||||
public static List<LocalDate> getDateList(LocalDate startDate, int days) {
|
||||
List<LocalDate> dateList = new ArrayList<>(days);
|
||||
for (int i = 0; i < days; i++) {
|
||||
dateList.add(startDate.plusDays(i));
|
||||
}
|
||||
return dateList;
|
||||
}
|
||||
|
||||
/**
|
||||
* 格式化时间范围
|
||||
*
|
||||
|
|
|
|||
|
|
@ -204,4 +204,11 @@ public class HttpUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* WebSocket URL 切换成 HTTP URL:ws:// → http://;wss:// → https://;其它格式原样保留
|
||||
*/
|
||||
public static String wsUrlToHttp(String url) {
|
||||
return StrUtil.startWithIgnoreCase(url, "ws") ? "http" + url.substring(2) : url;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import java.lang.reflect.Type;
|
|||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* JSON 工具类
|
||||
|
|
@ -173,6 +174,20 @@ public class JsonUtils {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JSON 字符串成 Map,空字符串或解析失败返回 null
|
||||
*/
|
||||
public static Map<String, Object> parseMap(String text) {
|
||||
if (StrUtil.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return objectMapper.readValue(text, new TypeReference<Map<String, Object>>() {});
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析 JSON 字符串成指定类型的对象,如果解析失败,则返回 null
|
||||
*
|
||||
|
|
@ -235,6 +250,14 @@ public class JsonUtils {
|
|||
}
|
||||
}
|
||||
|
||||
public static String getText(JsonNode node, String fieldName) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
JsonNode value = node.get(fieldName);
|
||||
return value != null && !value.isNull() ? value.asText() : null;
|
||||
}
|
||||
|
||||
public static boolean isJson(String text) {
|
||||
return JSONUtil.isTypeJSON(text);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.framework.common.util.string;
|
|||
import cn.hutool.core.text.StrPool;
|
||||
import cn.hutool.core.util.ArrayUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.pinyin.PinyinUtil;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
@ -78,6 +79,16 @@ public class StrUtils {
|
|||
.collect(Collectors.joining("\n"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 转小写拼音,字之间以空格分隔,便于调用方按需拼接 / 取首字母 / 拼音搜索
|
||||
*/
|
||||
public static String toPinyin(String str) {
|
||||
if (StrUtil.isBlank(str)) {
|
||||
return null;
|
||||
}
|
||||
return PinyinUtil.getPinyin(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* 拼接方法的参数
|
||||
*
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- RPC 相关 -->
|
||||
|
|
|
|||
|
|
@ -42,8 +42,8 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,只有 ExcelUtils 使用 -->
|
||||
</dependency>
|
||||
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@
|
|||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
</dependencies>
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@
|
|||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
<scope>provided</scope> <!-- 设置为 provided,只有 TraceFilter 使用 -->
|
||||
</dependency>
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,13 @@
|
|||
<groupId>com.fhs-opensource</groupId>
|
||||
<artifactId>easy-trans-mybatis-plus-extend</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -119,6 +119,31 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
|||
return selectOne(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得满足条件的一条记录,并使用 FOR UPDATE 锁定。
|
||||
*
|
||||
* 注意:需要在事务中调用,否则锁会立即释放。
|
||||
*
|
||||
* @param queryWrapper 查询条件
|
||||
* @return 实体
|
||||
*/
|
||||
default T selectOneForUpdate(LambdaQueryWrapper<T> queryWrapper) {
|
||||
return selectOne(queryWrapper.last("FOR UPDATE"));
|
||||
}
|
||||
|
||||
default T selectOneForUpdate(SFunction<T, ?> field, Object value) {
|
||||
return selectOneForUpdate(new LambdaQueryWrapper<T>().eq(field, value));
|
||||
}
|
||||
|
||||
default T selectOneForUpdate(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2) {
|
||||
return selectOneForUpdate(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2));
|
||||
}
|
||||
|
||||
default T selectOneForUpdate(SFunction<T, ?> field1, Object value1, SFunction<T, ?> field2, Object value2,
|
||||
SFunction<T, ?> field3, Object value3) {
|
||||
return selectOneForUpdate(new LambdaQueryWrapper<T>().eq(field1, value1).eq(field2, value2).eq(field3, value3));
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取满足条件的第 1 条记录
|
||||
*
|
||||
|
|
@ -145,6 +170,14 @@ public interface BaseMapperX<T> extends MPJBaseMapper<T> {
|
|||
return CollUtil.getFirst(list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取满足条件的最新一条记录
|
||||
* <p>
|
||||
* 目的:解决并发场景下,插入多条记录后,使用 selectOne 会报错的问题
|
||||
*/
|
||||
default T selectLastOne(LambdaQueryWrapper<T> queryWrapper) {
|
||||
return CollUtil.getLast(selectList(queryWrapper));
|
||||
}
|
||||
|
||||
default Long selectCount() {
|
||||
return selectCount(new QueryWrapper<>());
|
||||
|
|
|
|||
|
|
@ -24,6 +24,12 @@ public class LambdaQueryWrapperX<T> extends LambdaQueryWrapper<T> {
|
|||
}
|
||||
return this;
|
||||
}
|
||||
public LambdaQueryWrapperX<T> likeRightIfPresent(SFunction<T, ?> column, String val) {
|
||||
if (StringUtils.hasText(val)) {
|
||||
return (LambdaQueryWrapperX<T>) super.likeRight(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public LambdaQueryWrapperX<T> inIfPresent(SFunction<T, ?> column, Collection<?> values) {
|
||||
if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
|
||||
|
|
|
|||
|
|
@ -27,6 +27,13 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public <S> MPJLambdaWrapperX<T> likeRightIfPresent(SFunction<S, ?> column, String val) {
|
||||
if (StringUtils.hasText(val)) {
|
||||
return (MPJLambdaWrapperX<T>) super.likeRight(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public <S> MPJLambdaWrapperX<T> inIfPresent(SFunction<S, ?> column, Collection<?> values) {
|
||||
if (ObjectUtil.isAllNotEmpty(values) && !ArrayUtil.isEmpty(values)) {
|
||||
return (MPJLambdaWrapperX<T>) super.in(column, values);
|
||||
|
|
@ -102,7 +109,6 @@ public class MPJLambdaWrapperX<T> extends MPJLambdaWrapper<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
|
||||
// ========== 重写父类方法,方便链式调用 ==========
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -25,6 +25,13 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
|
|||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> likeRightIfPresent(String column, String val) {
|
||||
if (StringUtils.hasText(val)) {
|
||||
return (QueryWrapperX<T>) super.likeRight(column, val);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public QueryWrapperX<T> inIfPresent(String column, Collection<?> values) {
|
||||
if (!CollectionUtils.isEmpty(values)) {
|
||||
return (QueryWrapperX<T>) super.in(column, values);
|
||||
|
|
@ -95,13 +102,13 @@ public class QueryWrapperX<T> extends QueryWrapper<T> {
|
|||
}
|
||||
|
||||
public QueryWrapperX<T> betweenIfPresent(String column, Object[] values) {
|
||||
if (values!= null && values.length != 0 && values[0] != null && values[1] != null) {
|
||||
if (values != null && values.length != 0 && values[0] != null && values[1] != null) {
|
||||
return (QueryWrapperX<T>) super.between(column, values[0], values[1]);
|
||||
}
|
||||
if (values!= null && values.length != 0 && values[0] != null) {
|
||||
if (values != null && values.length != 0 && values[0] != null) {
|
||||
return (QueryWrapperX<T>) ge(column, values[0]);
|
||||
}
|
||||
if (values!= null && values.length != 0 && values[1] != null) {
|
||||
if (values != null && values.length != 0 && values[1] != null) {
|
||||
return (QueryWrapperX<T>) le(column, values[1]);
|
||||
}
|
||||
return this;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import net.sf.jsqlparser.schema.Table;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
/**
|
||||
* MyBatis 工具类
|
||||
|
|
@ -31,6 +32,8 @@ public class MyBatisUtils {
|
|||
|
||||
private static final String MYSQL_ESCAPE_CHARACTER = "`";
|
||||
|
||||
private static final Pattern SAFE_COLUMN_NAME_PATTERN = Pattern.compile("^[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)*$");
|
||||
|
||||
public static <T> Page<T> buildPage(PageParam pageParam) {
|
||||
return buildPage(pageParam, null);
|
||||
}
|
||||
|
|
@ -42,8 +45,11 @@ public class MyBatisUtils {
|
|||
// 排序字段
|
||||
if (CollUtil.isNotEmpty(sortingFields)) {
|
||||
for (SortingField sortingField : sortingFields) {
|
||||
page.addOrder(new OrderItem().setAsc(SortingField.ORDER_ASC.equals(sortingField.getOrder()))
|
||||
.setColumn(StrUtil.toUnderlineCase(sortingField.getField())));
|
||||
String columnName = buildSafeOrderColumn(sortingField.getField());
|
||||
if (columnName == null) {
|
||||
continue;
|
||||
}
|
||||
page.addOrder(new OrderItem().setAsc(isAscOrder(sortingField.getOrder())).setColumn(columnName));
|
||||
}
|
||||
}
|
||||
return page;
|
||||
|
|
@ -57,23 +63,29 @@ public class MyBatisUtils {
|
|||
if (wrapper instanceof QueryWrapper) {
|
||||
QueryWrapper<T> query = (QueryWrapper<T>) wrapper;
|
||||
for (SortingField sortingField : sortingFields) {
|
||||
query.orderBy(true,
|
||||
SortingField.ORDER_ASC.equals(sortingField.getOrder()),
|
||||
StrUtil.toUnderlineCase(sortingField.getField()));
|
||||
String columnName = buildSafeOrderColumn(sortingField.getField());
|
||||
if (columnName == null) {
|
||||
continue;
|
||||
}
|
||||
query.orderBy(true, isAscOrder(sortingField.getOrder()), columnName);
|
||||
}
|
||||
} else if (wrapper instanceof LambdaQueryWrapper) {
|
||||
// LambdaQueryWrapper 不直接支持字符串字段排序,使用 last 方法拼接 ORDER BY
|
||||
LambdaQueryWrapper<T> lambdaQuery = (LambdaQueryWrapper<T>) wrapper;
|
||||
StringBuilder orderBy = new StringBuilder();
|
||||
for (SortingField sortingField : sortingFields) {
|
||||
String columnName = buildSafeOrderColumn(sortingField.getField());
|
||||
if (columnName == null) {
|
||||
continue;
|
||||
}
|
||||
if (StrUtil.isNotEmpty(orderBy)) {
|
||||
orderBy.append(", ");
|
||||
}
|
||||
orderBy.append(StrUtil.toUnderlineCase(sortingField.getField()))
|
||||
.append(" ")
|
||||
.append(SortingField.ORDER_ASC.equals(sortingField.getOrder()) ? "ASC" : "DESC");
|
||||
orderBy.append(columnName).append(" ").append(getOrderDirection(sortingField.getOrder()));
|
||||
}
|
||||
if (StrUtil.isNotEmpty(orderBy)) {
|
||||
lambdaQuery.last("ORDER BY " + orderBy);
|
||||
}
|
||||
lambdaQuery.last("ORDER BY " + orderBy);
|
||||
// 另外个思路:https://blog.csdn.net/m0_59084856/article/details/138450913
|
||||
} else {
|
||||
throw new IllegalArgumentException("Unsupported wrapper type: " + wrapper.getClass().getName());
|
||||
|
|
@ -81,6 +93,22 @@ public class MyBatisUtils {
|
|||
|
||||
}
|
||||
|
||||
public static boolean isAscOrder(String order) {
|
||||
return SortingField.ORDER_ASC.equals(order);
|
||||
}
|
||||
|
||||
public static String getOrderDirection(String order) {
|
||||
return isAscOrder(order) ? "ASC" : "DESC";
|
||||
}
|
||||
|
||||
private static String buildSafeOrderColumn(String field) {
|
||||
String columnName = StrUtil.toUnderlineCase(field);
|
||||
if (StrUtil.isEmpty(columnName) || !SAFE_COLUMN_NAME_PATTERN.matcher(columnName).matches()) {
|
||||
return null;
|
||||
}
|
||||
return columnName;
|
||||
}
|
||||
|
||||
/**
|
||||
* 将拦截器添加到链中
|
||||
* 由于 MybatisPlusInterceptor 不支持添加拦截器,所以只能全量设置
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
package cn.iocoder.yudao.framework.mybatis.core.util;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.pojo.SortingField;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.OrderItem;
|
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
/**
|
||||
* {@link MyBatisUtils} 的单元测试
|
||||
*/
|
||||
public class MyBatisUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testBuildPage_sortingFields() {
|
||||
// 准备参数
|
||||
PageParam pageParam = new PageParam();
|
||||
pageParam.setPageNo(2);
|
||||
pageParam.setPageSize(20);
|
||||
List<SortingField> sortingFields = Arrays.asList(
|
||||
new SortingField("userName", SortingField.ORDER_ASC),
|
||||
new SortingField("u.id", SortingField.ORDER_DESC),
|
||||
new SortingField("name desc", SortingField.ORDER_DESC));
|
||||
|
||||
// 调用
|
||||
Page<Object> page = MyBatisUtils.buildPage(pageParam, sortingFields);
|
||||
|
||||
// 断言
|
||||
assertEquals(2, page.getCurrent());
|
||||
assertEquals(20, page.getSize());
|
||||
assertEquals(2, page.orders().size());
|
||||
assertOrderItem(page.orders().get(0), "user_name", true);
|
||||
assertOrderItem(page.orders().get(1), "u.id", false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOrder_queryWrapper() {
|
||||
// 准备参数
|
||||
QueryWrapper<Object> query = new QueryWrapper<>();
|
||||
List<SortingField> sortingFields = Arrays.asList(
|
||||
new SortingField("userName", SortingField.ORDER_ASC),
|
||||
new SortingField("u.id", SortingField.ORDER_DESC),
|
||||
new SortingField("name;drop", SortingField.ORDER_ASC));
|
||||
|
||||
// 调用
|
||||
MyBatisUtils.addOrder(query, sortingFields);
|
||||
|
||||
// 断言
|
||||
assertEquals(" ORDER BY user_name ASC,u.id DESC", query.getSqlSegment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOrder_lambdaQueryWrapper() {
|
||||
// 准备参数
|
||||
LambdaQueryWrapper<Object> query = new LambdaQueryWrapper<>();
|
||||
List<SortingField> sortingFields = Arrays.asList(
|
||||
new SortingField("userName", SortingField.ORDER_ASC),
|
||||
new SortingField("u.id", SortingField.ORDER_DESC),
|
||||
new SortingField("name`", SortingField.ORDER_ASC));
|
||||
|
||||
// 调用
|
||||
MyBatisUtils.addOrder(query, sortingFields);
|
||||
|
||||
// 断言
|
||||
assertEquals(" ORDER BY user_name ASC, u.id DESC", query.getSqlSegment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddOrder_lambdaQueryWrapper_invalidSortingFields() {
|
||||
// 准备参数
|
||||
LambdaQueryWrapper<Object> query = new LambdaQueryWrapper<>();
|
||||
List<SortingField> sortingFields = Arrays.asList(
|
||||
new SortingField("name desc", SortingField.ORDER_ASC),
|
||||
new SortingField("name;drop", SortingField.ORDER_DESC));
|
||||
|
||||
// 调用
|
||||
MyBatisUtils.addOrder(query, sortingFields);
|
||||
|
||||
// 断言
|
||||
assertEquals("", query.getSqlSegment());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOrderDirection() {
|
||||
assertTrue(MyBatisUtils.isAscOrder(SortingField.ORDER_ASC));
|
||||
assertFalse(MyBatisUtils.isAscOrder(SortingField.ORDER_DESC));
|
||||
assertEquals("ASC", MyBatisUtils.getOrderDirection(SortingField.ORDER_ASC));
|
||||
assertEquals("DESC", MyBatisUtils.getOrderDirection(SortingField.ORDER_DESC));
|
||||
assertEquals("DESC", MyBatisUtils.getOrderDirection(null));
|
||||
}
|
||||
|
||||
private void assertOrderItem(OrderItem orderItem, String column, boolean asc) {
|
||||
assertEquals(column, orderItem.getColumn());
|
||||
assertEquals(asc, orderItem.isAsc());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -50,8 +50,8 @@
|
|||
|
||||
<!-- 工具相关 -->
|
||||
<dependency>
|
||||
<groupId>jakarta.validation</groupId>
|
||||
<artifactId>jakarta.validation-api</artifactId>
|
||||
<groupId>javax.validation</groupId>
|
||||
<artifactId>validation-api</artifactId>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ public class ApiAccessLogFilter extends ApiRequestFilter {
|
|||
LocalDateTime beginTime = LocalDateTime.now();
|
||||
// 提前获得参数,避免 XssFilter 过滤处理
|
||||
Map<String, String> queryString = ServletUtils.getParamMap(request);
|
||||
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;
|
||||
String requestBody = ServletUtils.getBody(request);
|
||||
|
||||
try {
|
||||
// 继续过滤器
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ public class ApiAccessLogInterceptor implements HandlerInterceptor {
|
|||
// 打印 request 日志
|
||||
if (!SpringUtils.isProd()) {
|
||||
Map<String, String> queryString = ServletUtils.getParamMap(request);
|
||||
String requestBody = ServletUtils.isJsonRequest(request) ? ServletUtils.getBody(request) : null;
|
||||
String requestBody = ServletUtils.getBody(request);
|
||||
if (CollUtil.isEmpty(queryString) && StrUtil.isEmpty(requestBody)) {
|
||||
log.info("[preHandle][开始请求 URL({}) 无参数]", request.getRequestURI());
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -37,8 +37,14 @@ public class BannerApplicationRunner implements ApplicationRunner {
|
|||
System.out.println("[商城系统 yudao-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
|
||||
// ERP 系统
|
||||
System.out.println("[ERP 系统 yudao-module-erp - 教程][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
|
||||
// WMS 仓库管理系统
|
||||
System.out.println("[WMS 仓库管理系统 yudao-module-wms - 教程][参考 https://cloud.iocoder.cn/wms/build/ 开启]");
|
||||
// CRM 系统
|
||||
System.out.println("[CRM 系统 yudao-module-crm - 教程][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
|
||||
// MES 系统
|
||||
System.out.println("[MES 系统 yudao-module-mes - 教程][参考 https://cloud.iocoder.cn/mes/build/ 开启]");
|
||||
// IM 即时通讯
|
||||
System.out.println("[IM 即时通讯 yudao-module-im - 教程][参考 https://cloud.iocoder.cn/im/build/ 开启]");
|
||||
// 微信公众号
|
||||
System.out.println("[微信公众号 yudao-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
|
||||
// 支付平台
|
||||
|
|
|
|||
|
|
@ -410,25 +410,43 @@ public class GlobalExceptionHandler {
|
|||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[ERP 系统 yudao-module-erp - 表结构未导入][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
|
||||
}
|
||||
// 6. CRM 系统
|
||||
// 6. WMS 仓库管理系统
|
||||
if (message.contains("wms_")) {
|
||||
log.error("[WMS 仓库管理系统 yudao-module-wms - 表结构未导入][参考 https://cloud.iocoder.cn/wms/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[WMS 仓库管理系统 yudao-module-wms - 表结构未导入][参考 https://cloud.iocoder.cn/wms/build/ 开启]");
|
||||
}
|
||||
// 7. CRM 系统
|
||||
if (message.contains("crm_")) {
|
||||
log.error("[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[CRM 系统 yudao-module-crm - 表结构未导入][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
|
||||
}
|
||||
// 7. 支付平台
|
||||
// 8. MES 系统
|
||||
if (message.contains("mes_")) {
|
||||
log.error("[MES 系统 yudao-module-mes - 表结构未导入][参考 https://cloud.iocoder.cn/mes/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[MES 系统 yudao-module-mes - 表结构未导入][参考 https://cloud.iocoder.cn/mes/build/ 开启]");
|
||||
}
|
||||
// 9. IM 即时通讯
|
||||
if (message.contains("im_")) {
|
||||
log.error("[IM 即时通讯 yudao-module-im - 表结构未导入][参考 https://cloud.iocoder.cn/im/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[IM 即时通讯 yudao-module-im - 表结构未导入][参考 https://cloud.iocoder.cn/im/build/ 开启]");
|
||||
}
|
||||
// 10. 支付平台
|
||||
if (message.contains("pay_")) {
|
||||
log.error("[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[支付模块 yudao-module-pay - 表结构未导入][参考 https://cloud.iocoder.cn/pay/build/ 开启]");
|
||||
}
|
||||
// 8. AI 大模型
|
||||
// 11. AI 大模型
|
||||
if (message.contains("ai_")) {
|
||||
log.error("[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
"[AI 大模型 yudao-module-ai - 表结构未导入][参考 https://cloud.iocoder.cn/ai/build/ 开启]");
|
||||
}
|
||||
// 9. IoT 物联网
|
||||
// 12. IoT 物联网
|
||||
if (message.contains("iot_")) {
|
||||
log.error("[IoT 物联网 yudao-module-iot - 表结构未导入][参考 https://doc.iocoder.cn/iot/build/ 开启]");
|
||||
return CommonResult.error(NOT_IMPLEMENTED.getCode(),
|
||||
|
|
|
|||
|
|
@ -40,11 +40,6 @@
|
|||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>javax.servlet</groupId>
|
||||
<artifactId>javax.servlet-api</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>com.github.xiaoymin</groupId> <!-- 接口文档 -->
|
||||
<artifactId>knife4j-gateway-spring-boot-starter</artifactId>
|
||||
|
|
|
|||
|
|
@ -39,8 +39,14 @@ public class BannerApplicationRunner implements ApplicationRunner {
|
|||
System.out.println("[商城系统 yudao-module-mall 教程][参考 https://cloud.iocoder.cn/mall/build/ 开启]");
|
||||
// ERP 系统
|
||||
System.out.println("[ERP 系统 yudao-module-erp - 教程][参考 https://cloud.iocoder.cn/erp/build/ 开启]");
|
||||
// WMS 仓库管理系统
|
||||
System.out.println("[WMS 仓库管理系统 yudao-module-wms - 教程][参考 https://cloud.iocoder.cn/wms/build/ 开启]");
|
||||
// CRM 系统
|
||||
System.out.println("[CRM 系统 yudao-module-crm - 教程][参考 https://cloud.iocoder.cn/crm/build/ 开启]");
|
||||
// MES 系统
|
||||
System.out.println("[MES 系统 yudao-module-mes - 教程][参考 https://cloud.iocoder.cn/mes/build/ 开启]");
|
||||
// IM 即时通讯
|
||||
System.out.println("[IM 即时通讯 yudao-module-im - 教程][参考 https://cloud.iocoder.cn/im/build/ 开启]");
|
||||
// 微信公众号
|
||||
System.out.println("[微信公众号 yudao-module-mp 教程][参考 https://cloud.iocoder.cn/mp/build/ 开启]");
|
||||
// 支付平台
|
||||
|
|
|
|||
|
|
@ -199,6 +199,20 @@ spring:
|
|||
- Path=/admin-api/mes/**
|
||||
filters:
|
||||
- RewritePath=/admin-api/mes/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
|
||||
## wms-server 服务
|
||||
- id: wms-admin-api # 路由的编号
|
||||
uri: grayLb://wms-server
|
||||
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
|
||||
- Path=/admin-api/wms/**
|
||||
filters:
|
||||
- RewritePath=/admin-api/wms/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
|
||||
## im-server 服务
|
||||
- id: im-admin-api # 路由的编号
|
||||
uri: grayLb://im-server
|
||||
predicates: # 断言,作为路由的匹配条件,对应 RouteDefinition 数组
|
||||
- Path=/admin-api/im/**
|
||||
filters:
|
||||
- RewritePath=/admin-api/im/v3/api-docs, /v3/api-docs # 配置,保证转发到 /v3/api-docs
|
||||
x-forwarded:
|
||||
prefix-enabled: false # 避免 Swagger 重复带上额外的 /admin-api/system 前缀
|
||||
default-filters: # 全局过滤器,对应 GatewayFilterDefinition 数组
|
||||
|
|
@ -261,6 +275,12 @@ knife4j:
|
|||
- name: mes-server
|
||||
service-name: mes-server
|
||||
url: /admin-api/mes/v3/api-docs
|
||||
- name: wms-server
|
||||
service-name: wms-server
|
||||
url: /admin-api/wms/v3/api-docs
|
||||
- name: im-server
|
||||
service-name: im-server
|
||||
url: /admin-api/im/v3/api-docs
|
||||
|
||||
--- #################### 芋道相关配置 ####################
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.form.BpmFormFi
|
|||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
|
||||
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import lombok.SneakyThrows;
|
||||
import org.flowable.common.engine.api.delegate.Expression;
|
||||
import org.flowable.common.engine.api.variable.VariableContainer;
|
||||
|
|
@ -27,6 +28,7 @@ import org.flowable.engine.runtime.ProcessInstance;
|
|||
import org.flowable.task.api.TaskInfo;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
|
|
@ -245,10 +247,10 @@ public class FlowableUtils {
|
|||
}
|
||||
|
||||
// 解析表单配置
|
||||
Map<String, BpmFormFieldVO> formFieldsMap = new HashMap<>();
|
||||
Map<String, BpmFormFieldVO> formFieldsMap = new LinkedHashMap<>();
|
||||
processDefinitionInfo.getFormFields().forEach(formFieldStr -> {
|
||||
BpmFormFieldVO formField = JsonUtils.parseObject(formFieldStr, BpmFormFieldVO.class);
|
||||
parseFormField(formField, formFieldsMap);
|
||||
JsonNode formFieldNode = JsonUtils.parseObject(formFieldStr, JsonNode.class);
|
||||
parseFormField(formFieldNode, formFieldsMap);
|
||||
});
|
||||
|
||||
// 情况一:当自定义了摘要
|
||||
|
|
@ -275,18 +277,32 @@ public class FlowableUtils {
|
|||
/**
|
||||
* 递归解析表单字段
|
||||
*/
|
||||
private static void parseFormField(BpmFormFieldVO formField, Map<String, BpmFormFieldVO> formFieldsMap) {
|
||||
if (formField == null) {
|
||||
private static void parseFormField(JsonNode formFieldNode, Map<String, BpmFormFieldVO> formFieldsMap) {
|
||||
if (formFieldNode == null || !formFieldNode.isObject()) {
|
||||
return;
|
||||
}
|
||||
// 如果存在 children -> 说明是布局组件
|
||||
if (formField.getChildren() != null && !formField.getChildren().isEmpty()) {
|
||||
for (BpmFormFieldVO child : formField.getChildren()) {
|
||||
|
||||
// 如果 children 里存在对象节点,说明是布局组件;字符串节点是分割线、标签、文字等展示组件内容,直接跳过。
|
||||
JsonNode children = formFieldNode.get("children");
|
||||
if (children != null && children.isArray() && children.size() > 0) {
|
||||
boolean hasObjectChild = false;
|
||||
for (JsonNode child : children) {
|
||||
if (!child.isObject()) {
|
||||
continue;
|
||||
}
|
||||
hasObjectChild = true;
|
||||
parseFormField(child, formFieldsMap);
|
||||
}
|
||||
return;
|
||||
if (hasObjectChild) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// 真实字段才加入 map
|
||||
BpmFormFieldVO formField = new BpmFormFieldVO()
|
||||
.setType(JsonUtils.getText(formFieldNode, "type"))
|
||||
.setField(JsonUtils.getText(formFieldNode, "field"))
|
||||
.setTitle(JsonUtils.getText(formFieldNode, "title"));
|
||||
if (StrUtil.isNotBlank(formField.getField())) {
|
||||
formFieldsMap.put(formField.getField(), formField);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ public class BpmTaskCandidateInvokerTest extends BaseMockitoUnitTest {
|
|||
when(adminUserApi.getUserMap(eq(asSet(1L, 2L)))).thenReturn(userMap);
|
||||
// mock 方法(empty)
|
||||
when(emptyStrategy.calculateUsersByActivity(same(bpmnModel), eq(activityId),
|
||||
eq(param), same(startUserId), same(processDefinitionId), same(processVariables)))
|
||||
eq(param), same(startUserId), same(processDefinitionId), same(processVariables)))
|
||||
.thenReturn(Sets.newSet(2L));
|
||||
|
||||
// 调用
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import org.mockito.InjectMocks;
|
|||
import org.mockito.Mock;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,167 @@
|
|||
package cn.iocoder.yudao.module.bpm.framework.flowable.core.util;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.core.KeyValue;
|
||||
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
|
||||
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
|
||||
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelFormTypeEnum;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
|
||||
/**
|
||||
* {@link FlowableUtils} 的单元测试。
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
class FlowableUtilsTest {
|
||||
|
||||
@Test
|
||||
public void testGetSummary_customSummary_parseDbFormFields() {
|
||||
// 准备参数:模拟 DB 中 form_fields 字段,列表里每个元素都是一个 form-create 字段 JSON。
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfo(dbFormFields(),
|
||||
summarySetting(true, "reason", "days", "notExists", "startTime"));
|
||||
Map<String, Object> processVariables = processVariables();
|
||||
|
||||
// 调用
|
||||
List<KeyValue<String, String>> summary = FlowableUtils.getSummary(processDefinitionInfo, processVariables);
|
||||
|
||||
// 断言
|
||||
assertEquals(Arrays.asList(
|
||||
new KeyValue<>("请假原因", "事假"),
|
||||
new KeyValue<>("请假天数", "3"),
|
||||
new KeyValue<>("开始时间", "2026-05-31 09:00:00")),
|
||||
summary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSummary_defaultSummary_parseFirstThreeFieldsByFormOrder() {
|
||||
// 准备参数:未开启自定义摘要时,默认取表单配置顺序里的前三个真实字段。
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfo(dbFormFields(), null);
|
||||
Map<String, Object> processVariables = processVariables();
|
||||
|
||||
// 调用
|
||||
List<KeyValue<String, String>> summary = FlowableUtils.getSummary(processDefinitionInfo, processVariables);
|
||||
|
||||
// 断言
|
||||
assertEquals(Arrays.asList(
|
||||
new KeyValue<>("请假原因", "事假"),
|
||||
new KeyValue<>("开始时间", "2026-05-31 09:00:00"),
|
||||
new KeyValue<>("请假天数", "3")),
|
||||
summary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSummary_summaryDisabled_useDefaultSummary() {
|
||||
// 准备参数:摘要设置存在但未启用时,仍走默认摘要逻辑。
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfo(dbFormFields(),
|
||||
summarySetting(false, "remark"));
|
||||
Map<String, Object> processVariables = processVariables();
|
||||
|
||||
// 调用
|
||||
List<KeyValue<String, String>> summary = FlowableUtils.getSummary(processDefinitionInfo, processVariables);
|
||||
|
||||
// 断言
|
||||
assertEquals(Arrays.asList(
|
||||
new KeyValue<>("请假原因", "事假"),
|
||||
new KeyValue<>("开始时间", "2026-05-31 09:00:00"),
|
||||
new KeyValue<>("请假天数", "3")),
|
||||
summary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSummary_displayComponentsOnly_returnEmpty() {
|
||||
// 准备参数:分割线、标签、文字等展示组件的 children 是字符串数组,不是表单字段对象。
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = processDefinitionInfo(Arrays.asList(
|
||||
DIVIDER_FIELD,
|
||||
TEXT_FIELD,
|
||||
TAG_FIELD), null);
|
||||
|
||||
// 调用
|
||||
List<KeyValue<String, String>> summary = FlowableUtils.getSummary(processDefinitionInfo,
|
||||
Collections.emptyMap());
|
||||
|
||||
// 断言
|
||||
assertEquals(Collections.emptyList(), summary);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetSummary_notNormalForm_returnNull() {
|
||||
// 准备参数
|
||||
BpmProcessDefinitionInfoDO processDefinitionInfo = BpmProcessDefinitionInfoDO.builder()
|
||||
.formType(BpmModelFormTypeEnum.CUSTOM.getType())
|
||||
.build();
|
||||
|
||||
// 调用 & 断言
|
||||
assertNull(FlowableUtils.getSummary(null, Collections.emptyMap()));
|
||||
assertNull(FlowableUtils.getSummary(processDefinitionInfo, Collections.emptyMap()));
|
||||
}
|
||||
|
||||
private static BpmProcessDefinitionInfoDO processDefinitionInfo(List<String> formFields,
|
||||
BpmModelMetaInfoVO.SummarySetting summarySetting) {
|
||||
return BpmProcessDefinitionInfoDO.builder()
|
||||
.formType(BpmModelFormTypeEnum.NORMAL.getType())
|
||||
.formFields(formFields)
|
||||
.summarySetting(summarySetting)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static BpmModelMetaInfoVO.SummarySetting summarySetting(Boolean enable, String... fields) {
|
||||
BpmModelMetaInfoVO.SummarySetting summarySetting = new BpmModelMetaInfoVO.SummarySetting();
|
||||
summarySetting.setEnable(enable);
|
||||
summarySetting.setSummary(Arrays.asList(fields));
|
||||
return summarySetting;
|
||||
}
|
||||
|
||||
private static List<String> dbFormFields() {
|
||||
return Arrays.asList(
|
||||
DIVIDER_FIELD,
|
||||
"{\"type\":\"input\",\"field\":\"reason\",\"title\":\"请假原因\",\"value\":\"\","
|
||||
+ "\"props\":{\"type\":\"textarea\",\"placeholder\":\"请输入请假原因\"},"
|
||||
+ "\"$required\":\"请输入请假原因\",\"_fc_id\":\"id_F1\",\"_fc_drag_tag\":\"input\","
|
||||
+ "\"hidden\":false,\"display\":true}",
|
||||
TEXT_FIELD,
|
||||
"{\"type\":\"elRow\",\"title\":\"栅格布局\",\"children\":["
|
||||
+ "{\"type\":\"elCol\",\"props\":{\"span\":12},\"children\":["
|
||||
+ "{\"type\":\"DatePicker\",\"field\":\"startTime\",\"title\":\"开始时间\","
|
||||
+ "\"props\":{\"type\":\"datetime\",\"placeholder\":\"请选择开始时间\"},"
|
||||
+ "\"_fc_id\":\"id_F2\",\"_fc_drag_tag\":\"datePicker\"}]},"
|
||||
+ "\"字段说明\","
|
||||
+ "{\"type\":\"elCol\",\"props\":{\"span\":12},\"children\":["
|
||||
+ "{\"type\":\"inputNumber\",\"field\":\"days\",\"title\":\"请假天数\","
|
||||
+ "\"props\":{\"min\":0,\"precision\":1},\"_fc_id\":\"id_F3\","
|
||||
+ "\"_fc_drag_tag\":\"inputNumber\"}]}],\"_fc_id\":\"id_LAYOUT\","
|
||||
+ "\"_fc_drag_tag\":\"row\"}",
|
||||
TAG_FIELD,
|
||||
"{\"type\":\"input\",\"field\":\"remark\",\"title\":\"备注\",\"value\":\"\","
|
||||
+ "\"props\":{\"placeholder\":\"请输入备注\"},\"_fc_id\":\"id_F4\","
|
||||
+ "\"_fc_drag_tag\":\"input\",\"hidden\":false,\"display\":true}");
|
||||
}
|
||||
|
||||
private static Map<String, Object> processVariables() {
|
||||
Map<String, Object> processVariables = new HashMap<>();
|
||||
processVariables.put("reason", "事假");
|
||||
processVariables.put("startTime", "2026-05-31 09:00:00");
|
||||
processVariables.put("days", 3);
|
||||
processVariables.put("remark", "下午到家");
|
||||
return processVariables;
|
||||
}
|
||||
|
||||
private static final String DIVIDER_FIELD = "{\"type\":\"elDivider\",\"children\":[\"基础信息\"],"
|
||||
+ "\"props\":{\"contentPosition\":\"left\"},\"_fc_id\":\"id_DIVIDER\","
|
||||
+ "\"_fc_drag_tag\":\"elDivider\"}";
|
||||
|
||||
private static final String TEXT_FIELD = "{\"type\":\"div\",\"children\":[\"请按实际情况填写\"],"
|
||||
+ "\"props\":{\"style\":{\"color\":\"#909399\"}},\"_fc_id\":\"id_TEXT\","
|
||||
+ "\"_fc_drag_tag\":\"text\"}";
|
||||
|
||||
private static final String TAG_FIELD = "{\"type\":\"elTag\",\"children\":[\"重要\"],"
|
||||
+ "\"props\":{\"type\":\"warning\"},\"_fc_id\":\"id_TAG\",\"_fc_drag_tag\":\"elTag\"}";
|
||||
|
||||
}
|
||||
|
|
@ -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-im-api</module>
|
||||
<module>yudao-module-im-server</module>
|
||||
</modules>
|
||||
<artifactId>yudao-module-im</artifactId>
|
||||
<packaging>pom</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
im 模块,我们放即时通讯业务。
|
||||
例如说:单聊、群聊、消息收发、消息撤回、消息已读等等
|
||||
</description>
|
||||
|
||||
</project>
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
<?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-im</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-im-api</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
im 模块 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,5 @@
|
|||
/**
|
||||
* @author anhaohao
|
||||
* @since 2024/3/9 下午8:59
|
||||
*/
|
||||
package cn.iocoder.yudao.module.im.api;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
## AdoptOpenJDK 停止发布 OpenJDK 二进制,而 Eclipse Temurin 是它的延伸,提供更好的稳定性
|
||||
## 感谢复旦核博士的建议!灰子哥,牛皮!
|
||||
FROM eclipse-temurin:21-jre
|
||||
|
||||
## 创建目录,并使用它作为工作目录
|
||||
RUN mkdir -p /yudao-module-im-server
|
||||
WORKDIR /yudao-module-im-server
|
||||
## 将后端项目的 Jar 文件,复制到镜像中
|
||||
COPY ./target/yudao-module-im-server.jar app.jar
|
||||
|
||||
## 设置 TZ 时区
|
||||
## 设置 JAVA_OPTS 环境变量,可通过 docker run -e "JAVA_OPTS=" 进行覆盖
|
||||
ENV TZ=Asia/Shanghai JAVA_OPTS="-Xms512m -Xmx512m"
|
||||
|
||||
## 暴露后端项目的 48093 端口
|
||||
EXPOSE 48093
|
||||
|
||||
## 启动后端项目
|
||||
CMD java ${JAVA_OPTS} -Djava.security.egd=file:/dev/./urandom -jar app.jar
|
||||
|
|
@ -0,0 +1,157 @@
|
|||
<?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-im</artifactId>
|
||||
<version>${revision}</version>
|
||||
</parent>
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<artifactId>yudao-module-im-server</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<name>${project.artifactId}</name>
|
||||
<description>
|
||||
im 模块,我们放即时通讯业务。
|
||||
例如说:单聊、群聊、消息收发、消息撤回、消息已读等等
|
||||
</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-im-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-infra-api</artifactId>
|
||||
<version>${revision}</version>
|
||||
</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>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-validation</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- DB 相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-mybatis</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-redis</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-mq</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Test 测试相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-test</artifactId>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
|
||||
<!-- 工具类相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-excel</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 敏感词检测:sensitive-word(trie 树高效匹配,支持忽略大小写 / 全半角 / 数字风格) -->
|
||||
<dependency>
|
||||
<groupId>com.github.houbb</groupId>
|
||||
<artifactId>sensitive-word</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 汉字转拼音:作为 hutool PinyinUtil 的底层引擎,业务侧调用 StrUtils.toPinyin 时按需引入 -->
|
||||
<dependency>
|
||||
<groupId>com.belerweb</groupId>
|
||||
<artifactId>pinyin4j</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- 监控相关 -->
|
||||
<dependency>
|
||||
<groupId>cn.iocoder.cloud</groupId>
|
||||
<artifactId>yudao-spring-boot-starter-monitor</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
</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,30 @@
|
|||
package cn.iocoder.yudao.module.im;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
|
||||
/**
|
||||
* 项目的启动类
|
||||
*
|
||||
* 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
* 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
* 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@SpringBootApplication
|
||||
public class ImServerApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
|
||||
SpringApplication.run(ImServerApplication.class, args);
|
||||
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
// 如果你碰到启动的问题,请认真阅读 https://cloud.iocoder.cn/quick-start/ 文章
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.channel;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.material.ImChannelMaterialRespVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMaterialDO;
|
||||
import cn.iocoder.yudao.module.im.service.channel.ImChannelMaterialService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RequestParam;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "用户 APP - IM 频道素材")
|
||||
@RestController
|
||||
@RequestMapping("/im/channel/material")
|
||||
@Validated
|
||||
public class ImChannelMaterialController {
|
||||
|
||||
@Resource
|
||||
private ImChannelMaterialService channelMaterialService;
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获取素材详情;用于客户端点击图文卡片渲染详情页")
|
||||
@Parameter(name = "id", description = "素材编号", required = true, example = "1024")
|
||||
public CommonResult<ImChannelMaterialRespVO> getMaterial(@RequestParam("id") Long id) {
|
||||
ImChannelMaterialDO material = channelMaterialService.validateMaterialExists(id);
|
||||
return success(BeanUtils.toBean(material, ImChannelMaterialRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.face;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.face.vo.pack.ImFacePackUserRespVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFacePackDO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFacePackItemDO;
|
||||
import cn.iocoder.yudao.module.im.service.face.ImFacePackItemService;
|
||||
import cn.iocoder.yudao.module.im.service.face.ImFacePackService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
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.convertMultiMap;
|
||||
|
||||
@Tag(name = "管理后台 - IM 表情包")
|
||||
@RestController
|
||||
@RequestMapping("/im/face-pack")
|
||||
@Validated
|
||||
public class ImFacePackController {
|
||||
|
||||
@Resource
|
||||
private ImFacePackService facePackService;
|
||||
@Resource
|
||||
private ImFacePackItemService facePackItemService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得启用的表情包列表(含表情)")
|
||||
public CommonResult<List<ImFacePackUserRespVO>> getFacePackList() {
|
||||
// 1.1 拉所有启用表情包
|
||||
List<ImFacePackDO> packs = facePackService.getEnabledFacePackList();
|
||||
if (packs.isEmpty()) {
|
||||
return success(List.of());
|
||||
}
|
||||
// 1.2 拉这些包下所有启用表情,按 packId 分组
|
||||
List<ImFacePackItemDO> items = facePackItemService.getEnabledItemListByPackIds(
|
||||
convertList(packs, ImFacePackDO::getId));
|
||||
Map<Long, List<ImFacePackItemDO>> itemsByPackId = convertMultiMap(items, ImFacePackItemDO::getPackId);
|
||||
|
||||
// 2. 拼装:BeanUtils 把 pack 字段映射 + 自己塞 items
|
||||
List<ImFacePackUserRespVO> result = convertList(packs, pack -> {
|
||||
ImFacePackUserRespVO vo = BeanUtils.toBean(pack, ImFacePackUserRespVO.class);
|
||||
vo.setItems(BeanUtils.toBean(itemsByPackId.getOrDefault(pack.getId(), List.of()), ImFacePackUserRespVO.Item.class));
|
||||
return vo;
|
||||
});
|
||||
return success(result);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.face;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem.ImFaceUserItemRespVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem.ImFaceUserItemSaveReqVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFaceUserItemDO;
|
||||
import cn.iocoder.yudao.module.im.service.face.ImFaceUserItemService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
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.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - IM 个人表情")
|
||||
@RestController
|
||||
@RequestMapping("/im/face-user-item")
|
||||
@Validated
|
||||
public class ImFaceUserItemController {
|
||||
|
||||
@Resource
|
||||
private ImFaceUserItemService faceUserItemService;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得我的个人表情列表")
|
||||
public CommonResult<List<ImFaceUserItemRespVO>> getFaceUserItemList() {
|
||||
List<ImFaceUserItemDO> items = faceUserItemService.getFaceUserItemList(getLoginUserId());
|
||||
return success(BeanUtils.toBean(items, ImFaceUserItemRespVO.class));
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "添加个人表情")
|
||||
public CommonResult<Long> createFaceUserItem(@Valid @RequestBody ImFaceUserItemSaveReqVO reqVO) {
|
||||
return success(faceUserItemService.createFaceUserItem(getLoginUserId(), reqVO));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除个人表情")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "4096")
|
||||
public CommonResult<Boolean> deleteFaceUserItem(@RequestParam("id") Long id) {
|
||||
faceUserItemService.deleteFaceUserItem(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.face.vo.pack;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "IM 表情包(用户端) Response VO")
|
||||
@Data
|
||||
public class ImFacePackUserRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "表情包名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "猫主子")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "表情包图标", example = "https://cdn.example.com/face/pack/cat.png")
|
||||
private String icon;
|
||||
|
||||
@Schema(description = "表情列表", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private List<Item> items;
|
||||
|
||||
@Schema(description = "IM 表情包项(用户端)")
|
||||
@Data
|
||||
public static class Item {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "表情图 URL", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "https://cdn.example.com/face/pack/cat-001.png")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "表情名", example = "狗头")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "渲染宽度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer width;
|
||||
|
||||
@Schema(description = "渲染高度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer height;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "IM 个人表情 Response VO")
|
||||
@Data
|
||||
public class ImFaceUserItemRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "表情图 URL", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "https://cdn.example.com/face/user/abc.gif")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "表情名", example = "狗头")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "渲染宽度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer width;
|
||||
|
||||
@Schema(description = "渲染高度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer height;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "IM 个人表情新增 Request VO")
|
||||
@Data
|
||||
public class ImFaceUserItemSaveReqVO {
|
||||
|
||||
@Schema(description = "表情图 URL", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "https://cdn.example.com/face/user/abc.gif")
|
||||
@NotBlank(message = "表情图 URL 不能为空")
|
||||
@Size(max = 512, message = "表情图 URL 长度不能超过 512")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "表情名", example = "狗头")
|
||||
@Size(max = 64, message = "表情名长度不能超过 64")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "渲染宽度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
@NotNull(message = "渲染宽度不能为空")
|
||||
@Min(value = 1, message = "渲染宽度不能小于 1 像素")
|
||||
@Max(value = 2048, message = "渲染宽度不能大于 2048 像素")
|
||||
private Integer width;
|
||||
|
||||
@Schema(description = "渲染高度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
@NotNull(message = "渲染高度不能为空")
|
||||
@Min(value = 1, message = "渲染高度不能小于 1 像素")
|
||||
@Max(value = 2048, message = "渲染高度不能大于 2048 像素")
|
||||
private Integer height;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,127 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.friend;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.friend.vo.ImFriendRespVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.friend.vo.ImFriendUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendDO;
|
||||
import cn.iocoder.yudao.module.im.service.friend.ImFriendService;
|
||||
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.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.Collection;
|
||||
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.singleton;
|
||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - IM 好友")
|
||||
@RestController
|
||||
@RequestMapping("/im/friend")
|
||||
@Validated
|
||||
public class ImFriendController {
|
||||
|
||||
@Resource
|
||||
private ImFriendService friendService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得当前登录用户的好友列表")
|
||||
public CommonResult<List<ImFriendRespVO>> getMyFriendList() {
|
||||
// 含 DISABLE 历史好友:保留给前端展示「已删除好友」的历史对话信息;前端按 status 决定会话级联清理
|
||||
List<ImFriendDO> friends = friendService.getFriendList(getLoginUserId());
|
||||
return success(buildFriendRespVOList(friends));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得好友详情")
|
||||
@Parameter(name = "friendUserId", description = "好友的用户编号", required = true, example = "2048")
|
||||
public CommonResult<ImFriendRespVO> getFriend(@RequestParam("friendUserId") Long friendUserId) {
|
||||
ImFriendDO friend = friendService.getFriend(getLoginUserId(), friendUserId);
|
||||
return success(buildFriendRespVO(friend));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除好友(单向软删除)")
|
||||
@Parameters({
|
||||
@Parameter(description = "好友的用户编号", required = true, example = "2048"),
|
||||
@Parameter(description = "是否级联清理本端相关数据(如私聊会话)")
|
||||
})
|
||||
public CommonResult<Boolean> deleteFriend(
|
||||
@RequestParam("friendUserId") @NotNull(message = "好友用户编号不能为空") Long friendUserId,
|
||||
@RequestParam(value = "clear", required = false) Boolean clear) {
|
||||
friendService.deleteFriend(getLoginUserId(), friendUserId, clear);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新好友单边属性(备注 / 免打扰 / 联系人置顶)")
|
||||
public CommonResult<Boolean> updateFriend(@Valid @RequestBody ImFriendUpdateReqVO reqVO) {
|
||||
friendService.updateFriend(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/block")
|
||||
@Operation(summary = "拉黑好友(必须先是好友;单边屏蔽对方私聊消息)")
|
||||
@Parameter(name = "friendUserId", description = "好友的用户编号", required = true, example = "2048")
|
||||
public CommonResult<Boolean> blockFriend(
|
||||
@RequestParam("friendUserId") @NotNull(message = "好友用户编号不能为空") Long friendUserId) {
|
||||
friendService.blockFriend(getLoginUserId(), friendUserId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/unblock")
|
||||
@Operation(summary = "移出黑名单")
|
||||
@Parameter(name = "friendUserId", description = "好友的用户编号", required = true, example = "2048")
|
||||
public CommonResult<Boolean> unblockFriend(
|
||||
@RequestParam("friendUserId") @NotNull(message = "好友用户编号不能为空") Long friendUserId) {
|
||||
friendService.unblockFriend(getLoginUserId(), friendUserId);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ========== 私有方法:VO 组装 ==========
|
||||
|
||||
private List<ImFriendRespVO> buildFriendRespVOList(Collection<ImFriendDO> friends) {
|
||||
if (CollUtil.isEmpty(friends)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 批量聚合 AdminUser 信息(昵称 / 头像),避免 N+1
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertList(friends, ImFriendDO::getFriendUserId));
|
||||
return convertList(friends, friend -> {
|
||||
ImFriendRespVO vo = BeanUtils.toBean(friend, ImFriendRespVO.class);
|
||||
MapUtils.findAndThen(userMap, friend.getFriendUserId(), user ->
|
||||
vo.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
|
||||
// 备注 / 昵称的拼音,给前端做字母分桶 + 拼音搜索
|
||||
vo.setDisplayNamePinyin(StrUtils.toPinyin(vo.getDisplayName()))
|
||||
.setNicknamePinyin(StrUtils.toPinyin(vo.getNickname()));
|
||||
return vo;
|
||||
});
|
||||
}
|
||||
|
||||
private ImFriendRespVO buildFriendRespVO(ImFriendDO friend) {
|
||||
if (friend == null) {
|
||||
return null;
|
||||
}
|
||||
return CollUtil.getFirst(buildFriendRespVOList(singleton(friend)));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.friend;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.friend.vo.request.ImFriendRequestApplyReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.friend.vo.request.ImFriendRequestRespVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.friend.ImFriendRequestDO;
|
||||
import cn.iocoder.yudao.module.im.service.friend.ImFriendRequestService;
|
||||
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 javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
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 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.convertSetByFlatMap;
|
||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
/**
|
||||
* IM 好友申请记录 Controller
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Tag(name = "管理后台 - IM 好友申请")
|
||||
@RestController
|
||||
@RequestMapping("/im/friend-request")
|
||||
@Validated
|
||||
public class ImFriendRequestController {
|
||||
|
||||
@Resource
|
||||
private ImFriendRequestService friendRequestService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/apply")
|
||||
@Operation(summary = "发起好友申请")
|
||||
public CommonResult<Long> applyFriend(@Valid @RequestBody ImFriendRequestApplyReqVO reqVO) {
|
||||
ImFriendRequestDO request = friendRequestService.applyFriend(getLoginUserId(), reqVO);
|
||||
return success(request != null ? request.getId() : null);
|
||||
}
|
||||
|
||||
@PutMapping("/agree")
|
||||
@Operation(summary = "同意好友申请")
|
||||
@Parameter(name = "id", description = "申请编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> agreeFriendRequest(
|
||||
@RequestParam("id") @NotNull(message = "申请编号不能为空") Long id) {
|
||||
friendRequestService.agreeFriendRequest(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/refuse")
|
||||
@Operation(summary = "拒绝好友申请")
|
||||
public CommonResult<Boolean> refuseFriendRequest(
|
||||
@RequestParam("id") @NotNull(message = "申请编号不能为空") Long id,
|
||||
@RequestParam(value = "handleContent", required = false)
|
||||
@Size(max = 255, message = "处理理由最多 255 个字符") String handleContent) {
|
||||
friendRequestService.refuseFriendRequest(getLoginUserId(), id, handleContent);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "查询「我相关」的好友申请列表(游标分页:传 maxId 加载更多)")
|
||||
public CommonResult<List<ImFriendRequestRespVO>> getMyFriendRequestList(
|
||||
@Parameter(description = "当前列表最旧记录的 id;首页不传")
|
||||
@RequestParam(value = "maxId", required = false) Long maxId,
|
||||
@Parameter(description = "单次拉取条数", required = true)
|
||||
@RequestParam("limit") @Min(1) @Max(200) Integer limit) {
|
||||
List<ImFriendRequestDO> list = friendRequestService.getMyFriendRequestList(getLoginUserId(), maxId, limit);
|
||||
return success(buildList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "按 id 单查「我相关」的申请记录(带越权过滤;WebSocket 通知到达后用)")
|
||||
@Parameter(name = "id", description = "申请记录编号", required = true)
|
||||
public CommonResult<ImFriendRequestRespVO> getMyFriendRequest(@RequestParam("id") Long id) {
|
||||
ImFriendRequestDO request = friendRequestService.getFriendRequest(id);
|
||||
// 越权过滤:fromUser / toUser 必有一方是当前用户,否则当不存在返回 null
|
||||
Long currentUserId = getLoginUserId();
|
||||
if (request == null || (ObjUtil.notEqual(request.getFromUserId(), currentUserId)
|
||||
&& ObjUtil.notEqual(request.getToUserId(), currentUserId))) {
|
||||
return success(null);
|
||||
}
|
||||
return success(CollUtil.getFirst(buildList(Collections.singletonList(request))));
|
||||
}
|
||||
|
||||
// ========== 私有方法:VO 组装 ==========
|
||||
|
||||
private List<ImFriendRequestRespVO> buildList(List<ImFriendRequestDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 双向 OR 列表,userIds 取 from + to 两组并集
|
||||
Set<Long> userIds = convertSetByFlatMap(list,
|
||||
request -> Stream.of(request.getFromUserId(), request.getToUserId()));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
return convertList(list, request -> {
|
||||
ImFriendRequestRespVO vo = BeanUtils.toBean(request, ImFriendRequestRespVO.class);
|
||||
MapUtils.findAndThen(userMap, request.getFromUserId(), user ->
|
||||
vo.setFromNickname(user.getNickname()).setFromAvatar(user.getAvatar()));
|
||||
MapUtils.findAndThen(userMap, request.getToUserId(), user ->
|
||||
vo.setToNickname(user.getNickname()).setToAvatar(user.getAvatar()));
|
||||
return vo;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,61 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.friend.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* IM 好友 Response VO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Schema(description = "管理后台 - IM 好友 Response VO")
|
||||
@Data
|
||||
public class ImFriendRespVO {
|
||||
|
||||
@Schema(description = "关系记录编号", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "好友的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long friendUserId;
|
||||
|
||||
@Schema(description = "是否免打扰", example = "false")
|
||||
private Boolean silent;
|
||||
|
||||
@Schema(description = "好友展示备注(仅自己可见)", example = "老张")
|
||||
private String displayName;
|
||||
|
||||
@Schema(description = "好友展示备注的拼音(小写无空格)", example = "laozhang")
|
||||
private String displayNamePinyin;
|
||||
|
||||
@Schema(description = "添加来源", example = "1")
|
||||
private Integer addSource; // 参见 ImFriendAddSourceEnum 枚举
|
||||
|
||||
@Schema(description = "是否置顶联系人", example = "false")
|
||||
private Boolean pinned;
|
||||
|
||||
@Schema(description = "是否拉黑(仅自己可见)", example = "false")
|
||||
private Boolean blocked;
|
||||
|
||||
@Schema(description = "好友状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "添加好友时间")
|
||||
private LocalDateTime addTime;
|
||||
|
||||
@Schema(description = "删除好友时间")
|
||||
private LocalDateTime deleteTime;
|
||||
|
||||
// ========== 下面是聚合字段,方便前端显示 ==========
|
||||
|
||||
@Schema(description = "好友昵称(实时聚合自 AdminUser)", example = "芋道")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "好友昵称的拼音(小写无空格)", example = "yudao")
|
||||
private String nicknamePinyin;
|
||||
|
||||
@Schema(description = "好友头像(实时聚合自 AdminUser)")
|
||||
private String avatar;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.friend.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - IM 好友更新 Request VO")
|
||||
@Data
|
||||
public class ImFriendUpdateReqVO {
|
||||
|
||||
@Schema(description = "好友的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
@NotNull(message = "好友用户编号不能为空")
|
||||
private Long friendUserId;
|
||||
|
||||
@Schema(description = "是否免打扰;不传表示不修改", example = "true")
|
||||
private Boolean silent;
|
||||
|
||||
@Schema(description = "好友展示备注(仅自己可见);不传表示不修改,传空串表示清空", example = "老张")
|
||||
@Size(max = 16, message = "好友备注最多 16 个字符")
|
||||
private String displayName;
|
||||
|
||||
@Schema(description = "是否置顶联系人;不传表示不修改", example = "true")
|
||||
private Boolean pinned;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.friend.vo.request;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.im.enums.friend.ImFriendAddSourceEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* IM 好友申请 - 发起 Request VO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Schema(description = "管理后台 - IM 好友申请发起 Request VO")
|
||||
@Data
|
||||
public class ImFriendRequestApplyReqVO {
|
||||
|
||||
@Schema(description = "接收方用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
@NotNull(message = "接收方用户编号不能为空")
|
||||
private Long toUserId;
|
||||
|
||||
@Schema(description = "申请理由", example = "我是芋艿(一种食材)")
|
||||
@Size(max = 255, message = "申请理由最多 255 个字符")
|
||||
private String applyContent;
|
||||
|
||||
@Schema(description = "对接收方的备注(仅自己可见)", example = "老张")
|
||||
@Size(max = 16, message = "好友备注最多 16 个字符")
|
||||
private String displayName;
|
||||
|
||||
@Schema(description = "添加来源", example = "1")
|
||||
@InEnum(ImFriendAddSourceEnum.class)
|
||||
private Integer addSource;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.friend.vo.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* IM 好友申请 Response VO
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Schema(description = "管理后台 - IM 好友申请 Response VO")
|
||||
@Data
|
||||
public class ImFriendRequestRespVO {
|
||||
|
||||
@Schema(description = "申请编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "发起方用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Long fromUserId;
|
||||
|
||||
@Schema(description = "接收方用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Long toUserId;
|
||||
|
||||
@Schema(description = "处理结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer handleResult; // 参见 ImFriendRequestHandleResultEnum 枚举
|
||||
|
||||
@Schema(description = "申请理由", example = "我是芋艿(一种食材)")
|
||||
private String applyContent;
|
||||
|
||||
@Schema(description = "处理理由(接收方拒绝时可选填)", example = "暂不通过")
|
||||
private String handleContent;
|
||||
|
||||
@Schema(description = "添加来源", example = "1")
|
||||
private Integer addSource; // 参见 ImFriendAddSourceEnum 枚举
|
||||
|
||||
@Schema(description = "处理时间")
|
||||
private LocalDateTime handleTime;
|
||||
|
||||
@Schema(description = "申请创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// ========== 下面是聚合字段,方便前端显示 ==========
|
||||
|
||||
@Schema(description = "发起方昵称(实时聚合自 AdminUser)", example = "芋道")
|
||||
private String fromNickname;
|
||||
|
||||
@Schema(description = "发起方头像(实时聚合自 AdminUser)")
|
||||
private String fromAvatar;
|
||||
|
||||
@Schema(description = "接收方昵称(实时聚合自 AdminUser)", example = "老张")
|
||||
private String toNickname;
|
||||
|
||||
@Schema(description = "接收方头像(实时聚合自 AdminUser)")
|
||||
private String toAvatar;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,206 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.group.vo.*;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberInviteReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRemoveReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageRespVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO;
|
||||
import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService;
|
||||
import cn.iocoder.yudao.module.im.service.group.ImGroupService;
|
||||
import cn.iocoder.yudao.module.im.service.message.ImGroupMessageService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
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.*;
|
||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
@Tag(name = "管理后台 - 群")
|
||||
@RestController
|
||||
@RequestMapping("/im/group")
|
||||
@Validated
|
||||
public class ImGroupController {
|
||||
|
||||
@Resource
|
||||
private ImGroupService groupService;
|
||||
@Resource
|
||||
private ImGroupMemberService groupMemberService;
|
||||
@Resource
|
||||
private ImGroupMessageService groupMessageService;
|
||||
|
||||
// ==================== 群的写操作 ====================
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建群")
|
||||
public CommonResult<ImGroupRespVO> createGroup(@Valid @RequestBody ImGroupCreateReqVO createReqVO) {
|
||||
ImGroupDO group = groupService.createGroup(createReqVO, getLoginUserId());
|
||||
// 新建群必无 pinnedMessages,跳过关联回填
|
||||
return success(BeanUtils.toBean(group, ImGroupRespVO.class));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新群")
|
||||
public CommonResult<ImGroupRespVO> updateGroup(@Valid @RequestBody ImGroupUpdateReqVO updateReqVO) {
|
||||
ImGroupDO group = groupService.updateGroup(updateReqVO, getLoginUserId());
|
||||
return success(buildGroupRespVO(group, getLoginUserId()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/dissolve")
|
||||
@Operation(summary = "解散群")
|
||||
@Parameter(name = "id", description = "群编号", required = true)
|
||||
public CommonResult<Boolean> dissolveGroup(@RequestParam("id") Long id) {
|
||||
groupService.dissolveGroup(id, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 群的读操作 ====================
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得群")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
public CommonResult<ImGroupRespVO> getGroup(@RequestParam("id") Long id) {
|
||||
ImGroupDO group = groupService.getGroup(id);
|
||||
return success(buildGroupRespVO(group, getLoginUserId()));
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得当前登录用户的群列表")
|
||||
public CommonResult<List<ImGroupRespVO>> getMyGroupList() {
|
||||
Long loginUserId = getLoginUserId();
|
||||
List<ImGroupDO> groups = groupService.getMyGroupList(loginUserId);
|
||||
return success(buildGroupRespVOList(groups, loginUserId));
|
||||
}
|
||||
|
||||
// ==================== 群成员的写操作 ====================
|
||||
|
||||
@PostMapping("/invite")
|
||||
@Operation(summary = "邀请用户加入群")
|
||||
public CommonResult<Boolean> inviteGroupMember(@Valid @RequestBody ImGroupMemberInviteReqVO inviteReqVO) {
|
||||
groupService.inviteGroupMember(getLoginUserId(), inviteReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/quit")
|
||||
@Operation(summary = "退出群")
|
||||
@Parameter(name = "groupId", description = "群编号", required = true)
|
||||
public CommonResult<Boolean> quitGroup(@RequestParam("groupId") Long groupId) {
|
||||
groupService.quitGroup(groupId, getLoginUserId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/kicking")
|
||||
@Operation(summary = "移除群成员")
|
||||
public CommonResult<Boolean> removeGroupMember(@Valid @RequestBody ImGroupMemberRemoveReqVO removeReqVO) {
|
||||
groupService.removeGroupMember(getLoginUserId(), removeReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/add-admin")
|
||||
@Operation(summary = "添加群管理员")
|
||||
public CommonResult<Boolean> addGroupAdmin(@Valid @RequestBody ImGroupAdminAddReqVO reqVO) {
|
||||
groupService.addGroupAdmin(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/remove-admin")
|
||||
@Operation(summary = "撤销群管理员")
|
||||
public CommonResult<Boolean> removeGroupAdmin(@Valid @RequestBody ImGroupAdminRemoveReqVO reqVO) {
|
||||
groupService.removeGroupAdmin(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/transfer-owner")
|
||||
@Operation(summary = "转让群主")
|
||||
public CommonResult<Boolean> transferGroupOwner(@Valid @RequestBody ImGroupTransferOwnerReqVO transferReqVO) {
|
||||
groupService.transferGroupOwner(getLoginUserId(), transferReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 群消息置顶 ====================
|
||||
|
||||
@PutMapping("/pin-message")
|
||||
@Operation(summary = "置顶群消息(群主 / 管理员)")
|
||||
public CommonResult<Boolean> pinGroupMessage(@Valid @RequestBody ImGroupMessagePinReqVO reqVO) {
|
||||
groupService.pinGroupMessage(getLoginUserId(), reqVO.getId(), reqVO.getMessageId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/unpin-message")
|
||||
@Operation(summary = "取消置顶群消息(群主 / 管理员)")
|
||||
public CommonResult<Boolean> unpinGroupMessage(@Valid @RequestBody ImGroupMessagePinReqVO reqVO) {
|
||||
groupService.unpinGroupMessage(getLoginUserId(), reqVO.getId(), reqVO.getMessageId());
|
||||
return success(true);
|
||||
}
|
||||
|
||||
// ==================== 群禁言 ====================
|
||||
|
||||
@PutMapping("/mute-all")
|
||||
@Operation(summary = "全群禁言 / 取消(群主 / 管理员)")
|
||||
public CommonResult<Boolean> muteAll(@Valid @RequestBody ImGroupMuteAllReqVO reqVO) {
|
||||
groupService.muteAll(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/mute-member")
|
||||
@Operation(summary = "禁言成员")
|
||||
public CommonResult<Boolean> muteMember(@Valid @RequestBody ImGroupMuteMemberReqVO reqVO) {
|
||||
groupService.muteMember(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/cancel-mute-member")
|
||||
@Operation(summary = "取消成员禁言")
|
||||
public CommonResult<Boolean> cancelMuteMember(@Valid @RequestBody ImGroupCancelMuteMemberReqVO reqVO) {
|
||||
groupService.cancelMuteMember(getLoginUserId(), reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
/** 单群转 VO + 关联回填 pinnedMessages(仅当登录用户是该群有效成员) */
|
||||
private ImGroupRespVO buildGroupRespVO(ImGroupDO group, Long loginUserId) {
|
||||
if (group == null) {
|
||||
return null;
|
||||
}
|
||||
return buildGroupRespVOList(Collections.singletonList(group), loginUserId).get(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* 群列表批量转 VO + 关联回填 pinnedMessages
|
||||
* <p>
|
||||
* 仅当登录用户是某群的有效成员时才回填该群的 pinnedMessages,避免非成员 / 已退群用户越权拿到置顶消息内容
|
||||
*/
|
||||
private List<ImGroupRespVO> buildGroupRespVOList(List<ImGroupDO> groups, Long loginUserId) {
|
||||
if (CollUtil.isEmpty(groups)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 仅当前用户是有效成员的群才允许回填置顶消息
|
||||
Set<Long> activeGroupIds = convertSet(
|
||||
groupMemberService.getActiveGroupMemberListByUserId(loginUserId), ImGroupMemberDO::getGroupId);
|
||||
Set<Long> allMessageIds = convertSetByFlatMap(groups, group -> activeGroupIds.contains(group.getId())
|
||||
? CollUtil.emptyIfNull(group.getPinnedMessageIds()).stream() : Stream.empty());
|
||||
Map<Long, ImGroupMessageDO> messageMap = groupMessageService.getGroupMessageMap(allMessageIds);
|
||||
// 转换输出
|
||||
return convertList(groups, group -> {
|
||||
ImGroupRespVO vo = BeanUtils.toBean(group, ImGroupRespVO.class);
|
||||
if (!activeGroupIds.contains(group.getId()) || CollUtil.isEmpty(group.getPinnedMessageIds())) {
|
||||
return vo;
|
||||
}
|
||||
// 按 pin 顺序输出,已被删除的消息(messageMap 没命中)跳过
|
||||
List<ImGroupMessageDO> pinnedMesages = convertList(group.getPinnedMessageIds(), messageMap::get);
|
||||
return vo.setPinnedMessages(BeanUtils.toBean(pinnedMesages, ImGroupMessageRespVO.class));
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,125 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberRespVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.group.vo.member.ImGroupMemberUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
|
||||
import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService;
|
||||
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.Parameters;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
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.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
import static cn.iocoder.yudao.module.im.enums.ErrorCodeConstants.GROUP_MEMBER_NOT_IN_GROUP;
|
||||
|
||||
@Tag(name = "管理后台 - 群成员")
|
||||
@RestController
|
||||
@RequestMapping("/im/group-member")
|
||||
@Validated
|
||||
public class ImGroupMemberController {
|
||||
|
||||
@Resource
|
||||
private ImGroupMemberService groupMemberService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "更新群成员")
|
||||
public CommonResult<Boolean> updateGroupMember(@Valid @RequestBody ImGroupMemberUpdateReqVO updateReqVO) {
|
||||
groupMemberService.updateGroupMember(getLoginUserId(), updateReqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得群成员")
|
||||
@Parameters({
|
||||
@Parameter(name = "id", description = "编号(与 groupId + userId 二选一)", example = "1024"),
|
||||
@Parameter(name = "groupId", description = "群编号(与 userId 配合查)", example = "1"),
|
||||
@Parameter(name = "userId", description = "用户编号(与 groupId 配合查)", example = "100")
|
||||
})
|
||||
public CommonResult<ImGroupMemberRespVO> getGroupMember(@RequestParam(value = "id", required = false) Long id,
|
||||
@RequestParam(value = "groupId", required = false) Long groupId,
|
||||
@RequestParam(value = "userId", required = false) Long userId) {
|
||||
// 1. 查询群成员
|
||||
ImGroupMemberDO member;
|
||||
if (id != null) {
|
||||
member = groupMemberService.getGroupMember(id);
|
||||
} else if (groupId != null && userId != null) {
|
||||
member = groupMemberService.getGroupMember(groupId, userId);
|
||||
} else {
|
||||
// 避免 selectByGroupIdAndUserId 收到 null 参数走全表扫 / 抛 SQL 异常
|
||||
throw new IllegalArgumentException("参数缺失:需传 id 或 (groupId, userId)");
|
||||
}
|
||||
if (member == null) {
|
||||
return success(null);
|
||||
}
|
||||
|
||||
// 2. 校验当前登录用户是该成员所在群的有效成员
|
||||
Long loginUserId = getLoginUserId();
|
||||
groupMemberService.validateMemberInGroup(member.getGroupId(), loginUserId);
|
||||
|
||||
// 3. 转化 VO
|
||||
ImGroupMemberRespVO memberVO = BeanUtils.toBean(member, ImGroupMemberRespVO.class);
|
||||
AdminUserRespDTO user = adminUserApi.getUser(member.getUserId()).getCheckedData();
|
||||
if (user != null) {
|
||||
memberVO.setNickname(user.getNickname()).setAvatar(user.getAvatar());
|
||||
}
|
||||
hidePrivateFieldsIfNotSelf(memberVO, member.getUserId(), loginUserId);
|
||||
return success(memberVO);
|
||||
}
|
||||
|
||||
@GetMapping("/list")
|
||||
@Operation(summary = "获得指定群的成员列表")
|
||||
@Parameter(name = "groupId", description = "群编号", required = true, example = "1024")
|
||||
public CommonResult<List<ImGroupMemberRespVO>> getGroupMemberList(@RequestParam("groupId") Long groupId) {
|
||||
// 1.1 查询群成员列表(包含 DISABLE 已退群的成员,不按时间过滤)
|
||||
// 说明:保留已退群成员,是为了前端展示历史消息时,仍能通过该接口拿到已退群成员的昵称 / 头像信息,避免显示为空
|
||||
List<ImGroupMemberDO> members = groupMemberService.getGroupMemberListByGroupId(groupId);
|
||||
// 1.2 校验当前登录用户是否为群的有效成员,非成员不可查看
|
||||
Long loginUserId = getLoginUserId();
|
||||
if (CollUtil.findOne(members, member -> loginUserId.equals(member.getUserId())
|
||||
&& CommonStatusEnum.ENABLE.getStatus().equals(member.getStatus())) == null) {
|
||||
throw exception(GROUP_MEMBER_NOT_IN_GROUP);
|
||||
}
|
||||
|
||||
// 2.批量聚合 AdminUser 信息(昵称 / 头像)
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
convertList(members, ImGroupMemberDO::getUserId));
|
||||
return success(convertList(members, m -> {
|
||||
ImGroupMemberRespVO vo = BeanUtils.toBean(m, ImGroupMemberRespVO.class);
|
||||
MapUtils.findAndThen(userMap, m.getUserId(), user ->
|
||||
vo.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
|
||||
hidePrivateFieldsIfNotSelf(vo, m.getUserId(), loginUserId);
|
||||
return vo;
|
||||
}));
|
||||
}
|
||||
|
||||
/**
|
||||
* 非本人查看时,置空成员的私人设置字段(groupRemark / silent)
|
||||
*/
|
||||
private void hidePrivateFieldsIfNotSelf(ImGroupMemberRespVO vo, Long memberUserId, Long loginUserId) {
|
||||
if (ObjUtil.notEqual(loginUserId, memberUserId)) {
|
||||
vo.setGroupRemark(null).setSilent(null);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,155 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group;
|
||||
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.group.vo.request.ImGroupRequestApplyReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.group.vo.request.ImGroupRequestRespVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupMemberDO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupRequestDO;
|
||||
import cn.iocoder.yudao.module.im.enums.group.ImGroupMemberRoleEnum;
|
||||
import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService;
|
||||
import cn.iocoder.yudao.module.im.service.group.ImGroupRequestService;
|
||||
import cn.iocoder.yudao.module.im.service.group.ImGroupService;
|
||||
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 javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.*;
|
||||
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.*;
|
||||
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
|
||||
|
||||
/**
|
||||
* IM 加群申请 Controller
|
||||
*
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Tag(name = "管理后台 - IM 加群申请")
|
||||
@RestController
|
||||
@RequestMapping("/im/group-request")
|
||||
@Validated
|
||||
public class ImGroupRequestController {
|
||||
|
||||
@Resource
|
||||
private ImGroupRequestService groupRequestService;
|
||||
@Resource
|
||||
private ImGroupService groupService;
|
||||
@Resource
|
||||
private ImGroupMemberService groupMemberService;
|
||||
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@PostMapping("/apply")
|
||||
@Operation(summary = "申请加群")
|
||||
public CommonResult<Long> applyJoinGroup(@Valid @RequestBody ImGroupRequestApplyReqVO reqVO) {
|
||||
ImGroupRequestDO request = groupRequestService.applyJoinGroup(getLoginUserId(), reqVO);
|
||||
return success(request != null ? request.getId() : null);
|
||||
}
|
||||
|
||||
@PutMapping("/agree")
|
||||
@Operation(summary = "同意加群申请(群主或管理员)")
|
||||
@Parameter(name = "id", description = "申请编号", required = true, example = "1024")
|
||||
public CommonResult<Boolean> agreeGroupRequest(
|
||||
@RequestParam("id") @NotNull(message = "申请编号不能为空") Long id) {
|
||||
groupRequestService.agreeGroupRequest(getLoginUserId(), id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PutMapping("/refuse")
|
||||
@Operation(summary = "拒绝加群申请(群主或管理员)")
|
||||
public CommonResult<Boolean> refuseGroupRequest(
|
||||
@RequestParam("id") @NotNull(message = "申请编号不能为空") Long id,
|
||||
@RequestParam(value = "handleContent", required = false)
|
||||
@Size(max = 255, message = "处理理由最多 255 个字符") String handleContent) {
|
||||
groupRequestService.refuseGroupRequest(getLoginUserId(), id, handleContent);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/unhandled-list")
|
||||
@Operation(summary = "查询「我管理的所有群」下的未处理加群申请列表(不分页);前端 store 据此派生横幅红点 + Drawer 列表")
|
||||
public CommonResult<List<ImGroupRequestRespVO>> getUnhandledRequestList() {
|
||||
List<ImGroupRequestDO> list = groupRequestService.getUnhandledRequestListByOwnerOrAdmin(getLoginUserId());
|
||||
return success(buildVOList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/list-by-group")
|
||||
@Operation(summary = "查询指定群下的全部加群申请(含已处理);仅群主 / 管理员可查")
|
||||
@Parameter(name = "groupId", description = "群编号", required = true, example = "1024")
|
||||
public CommonResult<List<ImGroupRequestRespVO>> getGroupRequestListByGroupId(
|
||||
@RequestParam("groupId") @NotNull(message = "群编号不能为空") Long groupId) {
|
||||
List<ImGroupRequestDO> list = groupRequestService.getGroupRequestListByGroupId(getLoginUserId(), groupId);
|
||||
return success(buildVOList(list));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "按 id 单查申请记录(带越权过滤;WebSocket 通知到达后用)")
|
||||
@Parameter(name = "id", description = "申请记录编号", required = true)
|
||||
public CommonResult<ImGroupRequestRespVO> getGroupRequest(@RequestParam("id") Long id) {
|
||||
ImGroupRequestDO request = groupRequestService.getGroupRequest(id);
|
||||
if (request == null) {
|
||||
return success(null);
|
||||
}
|
||||
// 越权过滤:申请人 / 邀请人 / 群主 / 管理员之外,当不存在返回 null
|
||||
Long currentUserId = getLoginUserId();
|
||||
boolean canSee = ObjUtil.equal(request.getUserId(), currentUserId)
|
||||
|| ObjUtil.equal(request.getInviterUserId(), currentUserId)
|
||||
|| isGroupOwnerOrAdmin(request.getGroupId(), currentUserId);
|
||||
if (!canSee) {
|
||||
return success(null);
|
||||
}
|
||||
|
||||
// 转换并返回
|
||||
return success(CollUtil.getFirst(buildVOList(Collections.singletonList(request))));
|
||||
}
|
||||
|
||||
/**
|
||||
* 当前用户是否该群的有效群主 / 管理员
|
||||
*/
|
||||
private boolean isGroupOwnerOrAdmin(Long groupId, Long userId) {
|
||||
ImGroupMemberDO member = groupMemberService.getGroupMember(groupId, userId);
|
||||
return member != null
|
||||
&& !CommonStatusEnum.DISABLE.getStatus().equals(member.getStatus())
|
||||
&& ImGroupMemberRoleEnum.isOwnerOrAdmin(member.getRole());
|
||||
}
|
||||
|
||||
/** 申请记录列表批量转 VO + 关联回填用户 / 群信息 */
|
||||
private List<ImGroupRequestRespVO> buildVOList(List<ImGroupRequestDO> list) {
|
||||
if (CollUtil.isEmpty(list)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
// 1. 聚合 user / inviter 用户信息;convertSetByFlatMap 内部已过滤 null
|
||||
Set<Long> userIds = convertSetByFlatMap(list,
|
||||
request -> Stream.of(request.getUserId(), request.getInviterUserId()));
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
|
||||
// 2. 聚合群信息(封禁 / 解散群也要回填,便于前端展示历史)
|
||||
Set<Long> groupIds = convertSet(list, ImGroupRequestDO::getGroupId);
|
||||
Map<Long, ImGroupDO> groupMap = groupService.getGroupMap(groupIds);
|
||||
return convertList(list, request -> {
|
||||
ImGroupRequestRespVO vo = BeanUtils.toBean(request, ImGroupRequestRespVO.class);
|
||||
MapUtils.findAndThen(userMap, request.getUserId(), user ->
|
||||
vo.setUserNickname(user.getNickname()).setUserAvatar(user.getAvatar()));
|
||||
MapUtils.findAndThen(userMap, request.getInviterUserId(), user ->
|
||||
vo.setInviterNickname(user.getNickname()).setInviterAvatar(user.getAvatar()));
|
||||
MapUtils.findAndThen(groupMap, request.getGroupId(), group ->
|
||||
vo.setGroupName(group.getName()).setGroupAvatar(group.getAvatar()));
|
||||
return vo;
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 添加群管理员 Request VO")
|
||||
@Data
|
||||
public class ImGroupAdminAddReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13279")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目标用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[101, 102]")
|
||||
@NotEmpty(message = "目标用户编号列表不能为空")
|
||||
private List<Long> userIds;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 撤销群管理员 Request VO")
|
||||
@Data
|
||||
public class ImGroupAdminRemoveReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13279")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "目标用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[101, 102]")
|
||||
@NotEmpty(message = "目标用户编号列表不能为空")
|
||||
private List<Long> userIds;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 取消成员禁言 Request VO")
|
||||
@Data
|
||||
public class ImGroupCancelMuteMemberReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "被取消禁言的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 群创建 Request VO")
|
||||
@Data
|
||||
public class ImGroupCreateReqVO {
|
||||
|
||||
@Schema(description = "群名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道技术交流群")
|
||||
@NotBlank(message = "群名称不能为空")
|
||||
@Size(max = 64, message = "群名称长度不能超过 64")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "初始成员用户编号列表(建群同时邀请的好友,不含创建者自己)", example = "[1024, 2048]")
|
||||
private List<Long> memberUserIds;
|
||||
|
||||
@Schema(description = "进群是否需群主 / 管理员审批;不传默认 false 自由进群", example = "false")
|
||||
private Boolean joinApproval;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 管理后台 - 群消息置顶 / 取消置顶 Request VO
|
||||
*/
|
||||
@Schema(description = "管理后台 - 群消息置顶 / 取消置顶 Request VO")
|
||||
@Data
|
||||
public class ImGroupMessagePinReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13279")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9527")
|
||||
@NotNull(message = "消息编号不能为空")
|
||||
private Long messageId;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 全群禁言 / 取消 Request VO")
|
||||
@Data
|
||||
public class ImGroupMuteAllReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "是否全群禁言", requiredMode = Schema.RequiredMode.REQUIRED, example = "true")
|
||||
@NotNull(message = "是否全群禁言不能为空")
|
||||
private Boolean mutedAll;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 成员禁言 Request VO")
|
||||
@Data
|
||||
public class ImGroupMuteMemberReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "被禁言的用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "禁言时长(秒),0 表示永久禁言", requiredMode = Schema.RequiredMode.REQUIRED, example = "600")
|
||||
@NotNull(message = "禁言时长不能为空")
|
||||
@Min(value = 0, message = "禁言时长不能小于 0 秒")
|
||||
private Integer mutedSeconds;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import lombok.*;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
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)
|
||||
@ToString(callSuper = true)
|
||||
public class ImGroupPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "群名称", example = "芋艿")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "群主用户编号", example = "31460")
|
||||
private Long ownerUserId;
|
||||
|
||||
@Schema(description = "群公告")
|
||||
private String notice;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,53 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageRespVO;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 群 Response VO")
|
||||
@Data
|
||||
public class ImGroupRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1003")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "群名称", example = "芋艿")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "群主用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31460")
|
||||
private Long ownerUserId;
|
||||
|
||||
@Schema(description = "群头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "群公告")
|
||||
private String notice;
|
||||
|
||||
@Schema(description = "是否封禁")
|
||||
private Boolean banned;
|
||||
|
||||
@Schema(description = "是否全群禁言")
|
||||
private Boolean mutedAll;
|
||||
|
||||
@Schema(description = "进群是否需群主 / 管理员审批", example = "false")
|
||||
private Boolean joinApproval;
|
||||
|
||||
@Schema(description = "封禁时间")
|
||||
private LocalDateTime bannedTime;
|
||||
|
||||
@Schema(description = "群状态", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "解散时间")
|
||||
private LocalDateTime dissolvedTime;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "群置顶消息列表,按 pin 顺序(最先置顶的在前);非该群有效成员时为空")
|
||||
private List<ImGroupMessageRespVO> pinnedMessages;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.*;
|
||||
import javax.validation.constraints.*;
|
||||
|
||||
@Schema(description = "管理后台 - 群新增/修改 Request VO")
|
||||
@Data
|
||||
public class ImGroupSaveReqVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1003")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "群名称", example = "芋艿")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "群主用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "31460")
|
||||
@NotNull(message = "群主用户编号不能为空")
|
||||
private Long ownerUserId;
|
||||
|
||||
@Schema(description = "群头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "群公告")
|
||||
private String notice;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 群主转让 Request VO")
|
||||
@Data
|
||||
public class ImGroupTransferOwnerReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13279")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "新群主用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "202")
|
||||
@NotNull(message = "新群主用户编号不能为空")
|
||||
private Long newOwnerUserId;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 群更新 Request VO")
|
||||
@Data
|
||||
public class ImGroupUpdateReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1003")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "群名称", example = "芋道技术交流群")
|
||||
@Size(max = 64, message = "群名称长度不能超过 64")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "群头像")
|
||||
@Size(max = 512, message = "群头像长度不能超过 512")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "群公告")
|
||||
@Size(max = 2048, message = "群公告长度不能超过 2048")
|
||||
private String notice;
|
||||
|
||||
@Schema(description = "进群是否需群主 / 管理员审批", example = "true")
|
||||
private Boolean joinApproval;
|
||||
|
||||
@AssertTrue(message = "群名称不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isNameValid() {
|
||||
return name == null || StrUtil.isNotBlank(name);
|
||||
}
|
||||
|
||||
@AssertTrue(message = "群头像不能为空")
|
||||
@JsonIgnore
|
||||
public boolean isAvatarValid() {
|
||||
return avatar == null || StrUtil.isNotBlank(avatar);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 群成员邀请 Request VO")
|
||||
@Data
|
||||
public class ImGroupMemberCreateReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13279")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21730")
|
||||
@NotNull(message = "用户编号不能为空")
|
||||
private Long userId;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 群成员邀请 Request VO")
|
||||
@Data
|
||||
public class ImGroupMemberInviteReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "13279")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "被邀请的用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3]")
|
||||
@NotEmpty(message = "被邀请的用户编号列表不能为空")
|
||||
private List<Long> memberUserIds;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "管理后台 - 群成员移除 Request VO")
|
||||
@Data
|
||||
public class ImGroupMemberRemoveReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "被移除的用户编号列表", requiredMode = Schema.RequiredMode.REQUIRED, example = "[1, 2, 3]")
|
||||
@NotEmpty(message = "被移除的用户编号列表不能为空")
|
||||
private List<Long> memberUserIds;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 群成员 Response VO")
|
||||
@Data
|
||||
public class ImGroupMemberRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "17071")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "群编号", example = "13279")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "21730")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "组内显示名", example = "芋艿")
|
||||
private String displayUserName;
|
||||
|
||||
@Schema(description = "群备注", example = "核心群")
|
||||
private String groupRemark;
|
||||
|
||||
@Schema(description = "是否免打扰")
|
||||
private Boolean silent;
|
||||
|
||||
@Schema(description = "成员状态", example = "0")
|
||||
private Integer status;
|
||||
|
||||
@Schema(description = "成员角色", example = "3")
|
||||
private Integer role; // 参见 ImGroupMemberRoleEnum 枚举类
|
||||
|
||||
@Schema(description = "入群时间")
|
||||
private LocalDateTime joinTime;
|
||||
|
||||
@Schema(description = "退群时间")
|
||||
private LocalDateTime quitTime;
|
||||
|
||||
@Schema(description = "禁言到期时间")
|
||||
private LocalDateTime muteEndTime;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// ========== 关联 AdminUser 的字段 ==========
|
||||
|
||||
@Schema(description = "用户昵称", example = "芋道")
|
||||
private String nickname;
|
||||
|
||||
@Schema(description = "用户头像")
|
||||
private String avatar;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import lombok.Data;
|
||||
import lombok.experimental.Accessors;
|
||||
|
||||
@Schema(description = "管理后台 - 群成员更新 Request VO")
|
||||
@Data
|
||||
@Accessors(chain = true)
|
||||
public class ImGroupMemberUpdateReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "群内昵称", example = "芋头")
|
||||
private String displayUserName;
|
||||
|
||||
@Schema(description = "群备注", example = "公司群")
|
||||
private String groupRemark;
|
||||
|
||||
@Schema(description = "是否免打扰")
|
||||
private Boolean silent;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,26 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo.request;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.im.enums.group.ImGroupAddSourceEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - IM 加群申请发起 Request VO")
|
||||
@Data
|
||||
public class ImGroupRequestApplyReqVO {
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "群编号不能为空")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "申请理由", example = "我是芋艿(一种食材)")
|
||||
@Size(max = 255, message = "申请理由最多 255 个字符")
|
||||
private String applyContent;
|
||||
|
||||
@Schema(description = "加入来源", example = "1")
|
||||
@InEnum(ImGroupAddSourceEnum.class)
|
||||
private Integer addSource;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,65 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.group.vo.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - IM 加群申请 Response VO")
|
||||
@Data
|
||||
public class ImGroupRequestRespVO {
|
||||
|
||||
@Schema(description = "申请编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long groupId;
|
||||
|
||||
@Schema(description = "申请人 / 被邀请人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
|
||||
private Long userId;
|
||||
|
||||
@Schema(description = "邀请人用户编号;NULL 表示用户主动申请", example = "200")
|
||||
private Long inviterUserId;
|
||||
|
||||
@Schema(description = "处理结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer handleResult; // 参见 ImGroupRequestHandleResultEnum 枚举
|
||||
|
||||
@Schema(description = "申请理由", example = "我想加入这个群")
|
||||
private String applyContent;
|
||||
|
||||
@Schema(description = "处理理由(拒绝时可选填)", example = "暂不通过")
|
||||
private String handleContent;
|
||||
|
||||
@Schema(description = "处理人用户编号", example = "31460")
|
||||
private Long handleUserId;
|
||||
|
||||
@Schema(description = "加入来源", example = "1")
|
||||
private Integer addSource; // 参见 ImGroupAddSourceEnum 枚举
|
||||
|
||||
@Schema(description = "处理时间")
|
||||
private LocalDateTime handleTime;
|
||||
|
||||
@Schema(description = "申请创建时间")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
// ========== 下面是聚合字段,方便前端显示 ==========
|
||||
|
||||
@Schema(description = "申请人 / 被邀请人昵称(实时聚合自 AdminUser)", example = "芋道")
|
||||
private String userNickname;
|
||||
|
||||
@Schema(description = "申请人 / 被邀请人头像(实时聚合自 AdminUser)")
|
||||
private String userAvatar;
|
||||
|
||||
@Schema(description = "邀请人昵称(实时聚合自 AdminUser)", example = "老张")
|
||||
private String inviterNickname;
|
||||
|
||||
@Schema(description = "邀请人头像(实时聚合自 AdminUser)")
|
||||
private String inviterAvatar;
|
||||
|
||||
@Schema(description = "群名称(实时聚合自 ImGroup)", example = "芋道技术交流群")
|
||||
private String groupName;
|
||||
|
||||
@Schema(description = "群头像(实时聚合自 ImGroup)")
|
||||
private String groupAvatar;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.channel;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
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.im.controller.admin.manager.channel.vo.channel.ImChannelPageReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.channel.ImChannelRespVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.channel.ImChannelSaveReqVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelDO;
|
||||
import cn.iocoder.yudao.module.im.service.channel.ImChannelService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import javax.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;
|
||||
|
||||
@Tag(name = "管理后台 - IM 频道")
|
||||
@RestController
|
||||
@RequestMapping("/im/manager/channel")
|
||||
@Validated
|
||||
public class ImChannelManagerController {
|
||||
|
||||
@Resource
|
||||
private ImChannelService channelService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "新增频道")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel:create')")
|
||||
public CommonResult<Long> createChannel(@Valid @RequestBody ImChannelSaveReqVO reqVO) {
|
||||
return success(channelService.createChannel(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改频道")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel:update')")
|
||||
public CommonResult<Boolean> updateChannel(@Valid @RequestBody ImChannelSaveReqVO reqVO) {
|
||||
channelService.updateChannel(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除频道")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel:delete')")
|
||||
public CommonResult<Boolean> deleteChannel(@RequestParam("id") Long id) {
|
||||
channelService.deleteChannel(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得频道分页")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel:query')")
|
||||
public CommonResult<PageResult<ImChannelRespVO>> getChannelPage(@Valid ImChannelPageReqVO pageReqVO) {
|
||||
PageResult<ImChannelDO> pageResult = channelService.getChannelPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ImChannelRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得频道详情")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel:query')")
|
||||
public CommonResult<ImChannelRespVO> getChannel(@RequestParam("id") Long id) {
|
||||
ImChannelDO channel = channelService.getChannel(id);
|
||||
return success(BeanUtils.toBean(channel, ImChannelRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得启用的频道精简列表;前端表单选择频道时调用")
|
||||
public CommonResult<List<ImChannelRespVO>> getSimpleChannelList() {
|
||||
// TODO DONE @AI:getChannelListByStatus 统一命名
|
||||
List<ImChannelDO> list = channelService.getChannelListByStatus(CommonStatusEnum.ENABLE.getStatus());
|
||||
return success(BeanUtils.toBean(list, ImChannelRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,99 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.channel;
|
||||
|
||||
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.im.controller.admin.manager.channel.vo.material.ImChannelMaterialPageReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.material.ImChannelMaterialRespVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.material.ImChannelMaterialSaveReqVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelDO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelMaterialDO;
|
||||
import cn.iocoder.yudao.module.im.service.channel.ImChannelMaterialService;
|
||||
import cn.iocoder.yudao.module.im.service.channel.ImChannelService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import javax.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 static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
|
||||
@Tag(name = "管理后台 - IM 频道素材")
|
||||
@RestController
|
||||
@RequestMapping("/im/manager/channel-material")
|
||||
@Validated
|
||||
public class ImChannelMaterialManagerController {
|
||||
|
||||
@Resource
|
||||
private ImChannelMaterialService channelMaterialService;
|
||||
@Resource
|
||||
private ImChannelService channelService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "新增素材")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel-material:create')")
|
||||
public CommonResult<Long> createMaterial(@Valid @RequestBody ImChannelMaterialSaveReqVO reqVO) {
|
||||
return success(channelMaterialService.createMaterial(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改素材")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel-material:update')")
|
||||
public CommonResult<Boolean> updateMaterial(@Valid @RequestBody ImChannelMaterialSaveReqVO reqVO) {
|
||||
channelMaterialService.updateMaterial(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除素材")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel-material:delete')")
|
||||
public CommonResult<Boolean> deleteMaterial(@RequestParam("id") Long id) {
|
||||
channelMaterialService.deleteMaterial(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得素材分页;含频道名回填")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel-material:query')")
|
||||
public CommonResult<PageResult<ImChannelMaterialRespVO>> getMaterialPage(@Valid ImChannelMaterialPageReqVO pageReqVO) {
|
||||
PageResult<ImChannelMaterialDO> pageResult = channelMaterialService.getMaterialPage(pageReqVO);
|
||||
if (CollUtil.isEmpty(pageResult.getList())) {
|
||||
return success(PageResult.empty(pageResult.getTotal()));
|
||||
}
|
||||
// 回填频道名
|
||||
List<ImChannelDO> channels = channelService.getChannelList(
|
||||
convertSet(pageResult.getList(), ImChannelMaterialDO::getChannelId));
|
||||
Map<Long, ImChannelDO> channelMap = convertMap(channels, ImChannelDO::getId);
|
||||
return success(BeanUtils.toBean(pageResult, ImChannelMaterialRespVO.class, vo ->
|
||||
MapUtils.findAndThen(channelMap, vo.getChannelId(), c -> vo.setChannelName(c.getName()))));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得素材详情(含富文本正文)")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:channel-material:query')")
|
||||
public CommonResult<ImChannelMaterialRespVO> getMaterial(@RequestParam("id") Long id) {
|
||||
ImChannelMaterialDO material = channelMaterialService.getMaterial(id);
|
||||
return success(BeanUtils.toBean(material, ImChannelMaterialRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/simple-list")
|
||||
@Operation(summary = "获得指定频道下的素材精简列表;用于推送弹窗的素材下拉")
|
||||
@Parameter(name = "channelId", description = "频道编号", required = true, example = "1")
|
||||
public CommonResult<List<ImChannelMaterialRespVO>> getSimpleMaterialList(@RequestParam("channelId") Long channelId) {
|
||||
List<ImChannelMaterialDO> list = channelMaterialService.getMaterialListByChannelId(channelId);
|
||||
return success(BeanUtils.toBean(list, ImChannelMaterialRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.channel;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - IM 频道分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ImChannelPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "频道业务码", example = "system_notice")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "频道名称", example = "系统")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", example = "0")
|
||||
private Integer status; // 参见 CommonStatusEnum 枚举类
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.channel;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - IM 频道 Response VO")
|
||||
@Data
|
||||
public class ImChannelRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "频道业务码", requiredMode = Schema.RequiredMode.REQUIRED, example = "system_notice")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "频道名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "系统公告")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "频道头像")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer status; // 参见 CommonStatusEnum 枚举类
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.channel;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Pattern;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - IM 频道新增 / 修改 Request VO")
|
||||
@Data
|
||||
public class ImChannelSaveReqVO {
|
||||
|
||||
@Schema(description = "编号(修改时必填)", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "频道业务码;唯一", requiredMode = Schema.RequiredMode.REQUIRED, example = "system_notice")
|
||||
@NotBlank(message = "频道编码不能为空")
|
||||
@Size(max = 64, message = "频道编码长度不能超过 64")
|
||||
@Pattern(regexp = "^[a-z][a-z0-9_]*$", message = "频道编码只能由小写字母 / 数字 / 下划线组成,且必须以字母开头")
|
||||
private String code;
|
||||
|
||||
@Schema(description = "频道名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "系统公告")
|
||||
@NotBlank(message = "频道名称不能为空")
|
||||
@Size(max = 64, message = "频道名称长度不能超过 64")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "频道头像", example = "https://cdn.example.com/channel/system_notice.png")
|
||||
@Size(max = 512, message = "头像长度不能超过 512")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "排序不能为空")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "状态不能为空")
|
||||
private Integer status; // 参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用)
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.material;
|
||||
|
||||
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 = "管理后台 - IM 频道素材分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ImChannelMaterialPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "频道编号", example = "1")
|
||||
private Long channelId;
|
||||
|
||||
@Schema(description = "内容类型", example = "1")
|
||||
private Integer type; // 参见 ImChannelMaterialTypeEnum 枚举类
|
||||
|
||||
@Schema(description = "标题", example = "活动")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "创建时间", example = "[2026-04-01 00:00:00, 2026-04-30 23:59:59]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.material;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - IM 频道素材 Response VO")
|
||||
@Data
|
||||
public class ImChannelMaterialRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "频道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Long channelId;
|
||||
|
||||
@Schema(description = "频道名称(关联查询填充)")
|
||||
private String channelName;
|
||||
|
||||
@Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
private Integer type; // 参见 ImChannelMaterialTypeEnum 枚举类
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String title;
|
||||
|
||||
@Schema(description = "封面图")
|
||||
private String coverUrl;
|
||||
|
||||
@Schema(description = "摘要")
|
||||
private String summary;
|
||||
|
||||
@Schema(description = "正文;富文本 HTML")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "跳转链接")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.channel.vo.material;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - IM 频道素材新增 / 修改 Request VO")
|
||||
@Data
|
||||
public class ImChannelMaterialSaveReqVO {
|
||||
|
||||
@Schema(description = "编号(修改时必填)", example = "1024")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "频道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "频道编号不能为空")
|
||||
private Long channelId;
|
||||
|
||||
@Schema(description = "内容类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
|
||||
@NotNull(message = "内容类型不能为空")
|
||||
private Integer type; // 参见 ImChannelMaterialTypeEnum 枚举类
|
||||
|
||||
@Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED, example = "双十一活动来啦")
|
||||
@NotBlank(message = "标题不能为空")
|
||||
@Size(max = 128, message = "标题长度不能超过 128")
|
||||
private String title;
|
||||
|
||||
@Schema(description = "封面图", example = "https://cdn.example.com/cover.png")
|
||||
@Size(max = 512, message = "封面图长度不能超过 512")
|
||||
private String coverUrl;
|
||||
|
||||
@Schema(description = "摘要", example = "全场五折,戳详情看玩法")
|
||||
@Size(max = 255, message = "摘要长度不能超过 255")
|
||||
private String summary;
|
||||
|
||||
@Schema(description = "正文;富文本 HTML")
|
||||
private String content;
|
||||
|
||||
@Schema(description = "跳转链接;为空表示走客户端内置详情页", example = "https://example.com/activity/123")
|
||||
@Size(max = 512, message = "跳转链接长度不能超过 512")
|
||||
private String url;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.face;
|
||||
|
||||
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.im.controller.admin.manager.face.vo.item.ImFacePackItemPageReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.item.ImFacePackItemRespVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.item.ImFacePackItemSaveReqVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFacePackItemDO;
|
||||
import cn.iocoder.yudao.module.im.service.face.ImFacePackItemService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Size;
|
||||
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;
|
||||
|
||||
@Tag(name = "管理后台 - IM 表情包项")
|
||||
@RestController
|
||||
@RequestMapping("/im/manager/face-pack-item")
|
||||
@Validated
|
||||
public class ImFacePackItemManagerController {
|
||||
|
||||
@Resource
|
||||
private ImFacePackItemService facePackItemService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "新增表情")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack-item:create')")
|
||||
public CommonResult<Long> createFacePackItem(@Valid @RequestBody ImFacePackItemSaveReqVO reqVO) {
|
||||
return success(facePackItemService.createFacePackItem(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改表情")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack-item:update')")
|
||||
public CommonResult<Boolean> updateFacePackItem(@Valid @RequestBody ImFacePackItemSaveReqVO reqVO) {
|
||||
facePackItemService.updateFacePackItem(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除表情")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "2048")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack-item:delete')")
|
||||
public CommonResult<Boolean> deleteFacePackItem(@RequestParam("id") Long id) {
|
||||
facePackItemService.deleteFacePackItem(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Operation(summary = "批量删除表情")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack-item:delete')")
|
||||
public CommonResult<Boolean> deleteFacePackItemList(
|
||||
@RequestParam("ids") @Size(max = 100, message = "批量删除最多 100 条") List<Long> ids) {
|
||||
facePackItemService.deleteFacePackItemList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得表情分页")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack-item:query')")
|
||||
public CommonResult<PageResult<ImFacePackItemRespVO>> getFacePackItemPage(@Valid ImFacePackItemPageReqVO pageReqVO) {
|
||||
PageResult<ImFacePackItemDO> pageResult = facePackItemService.getFacePackItemPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ImFacePackItemRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得表情详情")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "2048")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack-item:query')")
|
||||
public CommonResult<ImFacePackItemRespVO> getFacePackItem(@RequestParam("id") Long id) {
|
||||
ImFacePackItemDO item = facePackItemService.getFacePackItem(id);
|
||||
return success(BeanUtils.toBean(item, ImFacePackItemRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,85 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.face;
|
||||
|
||||
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.im.controller.admin.manager.face.vo.pack.ImFacePackPageReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.pack.ImFacePackRespVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.pack.ImFacePackSaveReqVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFacePackDO;
|
||||
import cn.iocoder.yudao.module.im.service.face.ImFacePackService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import javax.annotation.Resource;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.Size;
|
||||
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;
|
||||
|
||||
@Tag(name = "管理后台 - IM 表情包")
|
||||
@RestController
|
||||
@RequestMapping("/im/manager/face-pack")
|
||||
@Validated
|
||||
public class ImFacePackManagerController {
|
||||
|
||||
@Resource
|
||||
private ImFacePackService facePackService;
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "新增表情包")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack:create')")
|
||||
public CommonResult<Long> createFacePack(@Valid @RequestBody ImFacePackSaveReqVO reqVO) {
|
||||
return success(facePackService.createFacePack(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/update")
|
||||
@Operation(summary = "修改表情包")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack:update')")
|
||||
public CommonResult<Boolean> updateFacePack(@Valid @RequestBody ImFacePackSaveReqVO reqVO) {
|
||||
facePackService.updateFacePack(reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除表情包")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack:delete')")
|
||||
public CommonResult<Boolean> deleteFacePack(@RequestParam("id") Long id) {
|
||||
facePackService.deleteFacePack(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete-list")
|
||||
@Operation(summary = "批量删除表情包")
|
||||
@Parameter(name = "ids", description = "编号列表", required = true)
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack:delete')")
|
||||
public CommonResult<Boolean> deleteFacePackList(@RequestParam("ids")
|
||||
@Size(max = 100, message = "批量删除最多 100 条") List<Long> ids) {
|
||||
facePackService.deleteFacePackList(ids);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得表情包分页")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack:query')")
|
||||
public CommonResult<PageResult<ImFacePackRespVO>> getFacePackPage(@Valid ImFacePackPageReqVO pageReqVO) {
|
||||
PageResult<ImFacePackDO> pageResult = facePackService.getFacePackPage(pageReqVO);
|
||||
return success(BeanUtils.toBean(pageResult, ImFacePackRespVO.class));
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得表情包详情")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-pack:query')")
|
||||
public CommonResult<ImFacePackRespVO> getFacePack(@RequestParam("id") Long id) {
|
||||
ImFacePackDO pack = facePackService.getFacePack(id);
|
||||
return success(BeanUtils.toBean(pack, ImFacePackRespVO.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.face;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem.ImFaceUserItemManagerPageReqVO;
|
||||
import cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem.ImFaceUserItemManagerRespVO;
|
||||
import cn.iocoder.yudao.module.im.dal.dataobject.face.ImFaceUserItemDO;
|
||||
import cn.iocoder.yudao.module.im.service.face.ImFaceUserItemService;
|
||||
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 javax.annotation.Resource;
|
||||
import javax.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 static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - IM 用户表情")
|
||||
@RestController
|
||||
@RequestMapping("/im/manager/face-user-item")
|
||||
@Validated
|
||||
public class ImFaceUserItemManagerController {
|
||||
|
||||
@Resource
|
||||
private ImFaceUserItemService faceUserItemService;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获得用户表情分页")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-user-item:query')")
|
||||
public CommonResult<PageResult<ImFaceUserItemManagerRespVO>> getFaceUserItemPage(
|
||||
@Valid ImFaceUserItemManagerPageReqVO pageReqVO) {
|
||||
PageResult<ImFaceUserItemDO> pageResult = faceUserItemService.getFaceUserItemPage(pageReqVO);
|
||||
// 关联回填用户昵称
|
||||
Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
|
||||
CollectionUtils.convertSet(pageResult.getList(), ImFaceUserItemDO::getUserId));
|
||||
List<ImFaceUserItemManagerRespVO> voList = CollectionUtils.convertList(pageResult.getList(), item -> {
|
||||
ImFaceUserItemManagerRespVO vo = BeanUtils.toBean(item, ImFaceUserItemManagerRespVO.class);
|
||||
AdminUserRespDTO user = userMap.get(item.getUserId());
|
||||
if (user != null) {
|
||||
vo.setUserNickname(user.getNickname());
|
||||
}
|
||||
return vo;
|
||||
});
|
||||
return success(new PageResult<>(voList, pageResult.getTotal()));
|
||||
}
|
||||
|
||||
@DeleteMapping("/delete")
|
||||
@Operation(summary = "删除用户表情")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "4096")
|
||||
@PreAuthorize("@ss.hasPermission('im:manager:face-user-item:delete')")
|
||||
public CommonResult<Boolean> deleteFaceUserItem(@RequestParam("id") Long id) {
|
||||
faceUserItemService.deleteFaceUserItem(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.item;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
@Schema(description = "管理后台 - IM 表情包项分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class ImFacePackItemPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "所属表情包编号", example = "1024")
|
||||
private Long packId;
|
||||
|
||||
@Schema(description = "表情名,模糊匹配", example = "狗")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "状态", example = "0")
|
||||
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
|
||||
private Integer status; // 参见 CommonStatusEnum 枚举类
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.item;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - IM 表情包项 Response VO")
|
||||
@Data
|
||||
public class ImFacePackItemRespVO {
|
||||
|
||||
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "所属表情包编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
private Long packId;
|
||||
|
||||
@Schema(description = "表情图 URL", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "https://cdn.example.com/face/pack/cat-001.png")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "表情名", example = "狗头")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "渲染宽度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer width;
|
||||
|
||||
@Schema(description = "渲染高度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
private Integer height;
|
||||
|
||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
private Integer status; // 参见 CommonStatusEnum 枚举类
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.item;
|
||||
|
||||
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 javax.validation.constraints.Max;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotBlank;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import javax.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - IM 表情包项新增 / 修改 Request VO")
|
||||
@Data
|
||||
public class ImFacePackItemSaveReqVO {
|
||||
|
||||
@Schema(description = "编号(修改时必填)", example = "2048")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "所属表情包编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
|
||||
@NotNull(message = "表情包编号不能为空")
|
||||
private Long packId;
|
||||
|
||||
@Schema(description = "表情图 URL", requiredMode = Schema.RequiredMode.REQUIRED,
|
||||
example = "https://cdn.example.com/face/pack/cat-001.png")
|
||||
@NotBlank(message = "表情图 URL 不能为空")
|
||||
@Size(max = 512, message = "表情图 URL 长度不能超过 512")
|
||||
private String url;
|
||||
|
||||
@Schema(description = "表情名", example = "狗头")
|
||||
@Size(max = 64, message = "表情名长度不能超过 64")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "渲染宽度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
@NotNull(message = "渲染宽度不能为空")
|
||||
@Min(value = 1, message = "渲染宽度不能小于 1 像素")
|
||||
@Max(value = 2048, message = "渲染宽度不能大于 2048 像素")
|
||||
private Integer width;
|
||||
|
||||
@Schema(description = "渲染高度(像素)", requiredMode = Schema.RequiredMode.REQUIRED, example = "200")
|
||||
@NotNull(message = "渲染高度不能为空")
|
||||
@Min(value = 1, message = "渲染高度不能小于 1 像素")
|
||||
@Max(value = 2048, message = "渲染高度不能大于 2048 像素")
|
||||
private Integer height;
|
||||
|
||||
@Schema(description = "排序", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "排序不能为空")
|
||||
private Integer sort;
|
||||
|
||||
@Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
|
||||
@NotNull(message = "状态不能为空")
|
||||
@InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
|
||||
private Integer status; // 参见 CommonStatusEnum 枚举类(0 启用 / 1 禁用)
|
||||
|
||||
}
|
||||