2026-06-30 24681c81c09022f584a57006f2534b5f74723414
初始化项目
已添加381个文件
已修改339个文件
已删除129个文件
71529 ■■■■■ 文件已修改
.gitignore 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/im-feature.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/im-preview-home.png 补丁 | 查看 | 原始文档 | blame | 历史
.image/common/im-preview-manager.png 补丁 | 查看 | 原始文档 | blame | 历史
README.md 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/123.png 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch1/database_changes.sql 166 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch1/frontend_changes.md 321 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch2/database_changes.sql 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch2/frontend_changes.md 229 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch3/database_changes.sql 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch3/frontend_changes.md 181 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch4/database_changes.sql 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch4/frontend_changes.md 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch5/database_changes.sql 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch5/frontend_changes.md 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch6/database_changes.sql 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/batch6/frontend_changes.md 163 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/cross_module_integration_plan.md 531 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/database_schema.sql 5152 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/im_module.sql 353 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/im_module_unlock.md 399 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/mdm_frontend_change.md 241 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/mes_wm_schema.sql 1212 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
docs/workflow_guide.md 771 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
pom.xml 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/livekit-poc/README.md 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/livekit-poc/docker-compose.yml 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/livekit-poc/livekit.yaml 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
script/livekit-poc/verify.sh 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/dm/ruoyi-vue-pro-dm8.sql 651 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/highgo/quartz.sql 208 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/highgo/ruoyi-vue-pro.sql 6198 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/kingbase/ruoyi-vue-pro.sql 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mdm_migration.sql 405 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mdm_tables.sql 334 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/ai.sql 332 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/mysql/ruoyi-vue-pro.sql 857 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/opengauss/ruoyi-vue-pro.sql 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/oracle/ruoyi-vue-pro.sql 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/postgresql/ruoyi-vue-pro.sql 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/sqlserver/ruoyi-vue-pro.sql 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/tools/README.md 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/tools/convertor.py 123 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
sql/tools/docker-compose.yaml 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-dependencies/pom.xml 69 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/http/HttpUtilsTest.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java 70 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQConsumerAutoConfiguration.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisStreamMessageCleanupJob.java 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/enums/DbTypeEnum.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java 88 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/translate/config/YudaoTranslateAutoConfiguration.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java 173 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java 112 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/handler/JsonWebSocketMessageHandler.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/security/LoginUserHandshakeInterceptor.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/pom.xml 40 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java 283 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java 122 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java 437 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/gemini/GeminiChatModel.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/stepfun/StepFunChatModel.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java 136 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java 75 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GrokChatModelTests.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java 85 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java 28 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/StepFunChatModelTests.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java 29 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/SiliconFlowImageModelTests.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/TongYiImagesModelTest.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/ZhiPuAiImageModelTests.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmAttachmentTypeEnum.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java 34 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java 247 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtilsTest.java 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerLimitConfigMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/pom.xml 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductPageReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInDO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutDO.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java 279 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInServiceImpl.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnServiceImpl.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseInStatusListener.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseOrderStatusListener.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutServiceImpl.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnServiceImpl.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ErpSaleOrderStatusListener.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ErpSaleOutStatusListener.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java 12 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java 114 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java 125 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockRecordCreateReqBO.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/pom.xml 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/api/package-info.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/ImChannelMaterialController.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/ImConversationReadController.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/vo/ImConversationReadRespVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/ImFacePackController.java 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/ImFaceUserItemController.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/pack/ImFacePackUserRespVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/userItem/ImFaceUserItemRespVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/userItem/ImFaceUserItemSaveReqVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/ImFriendController.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/ImFriendRequestController.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/vo/ImFriendRespVO.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/vo/ImFriendUpdateReqVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/vo/request/ImFriendRequestApplyReqVO.java 35 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/vo/request/ImFriendRequestRespVO.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupController.java 215 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupMemberController.java 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupRequestController.java 174 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupAdminAddReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupAdminRemoveReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupCancelMuteMemberReqVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupCreateReqVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupMessagePinReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupMuteAllReqVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupMuteMemberReqVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupPageReqVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupRespVO.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupSaveReqVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupTransferOwnerReqVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupUpdateReqVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberCreateReqVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberInviteReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberRemoveReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberRespVO.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberUpdateReqVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/request/ImGroupRequestApplyReqVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/request/ImGroupRequestRespVO.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelManagerController.java 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelMaterialManagerController.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/channel/ImChannelPageReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/channel/ImChannelRespVO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/channel/ImChannelSaveReqVO.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialPageReqVO.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialRespVO.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialSaveReqVO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackItemManagerController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackManagerController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFaceUserItemManagerController.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemPageReqVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemRespVO.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemSaveReqVO.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackPageReqVO.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackRespVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackSaveReqVO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/useritem/ImFaceUserItemManagerPageReqVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/useritem/ImFaceUserItemManagerRespVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/ImFriendManagerController.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/ImFriendRequestManagerController.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/vo/ImFriendManagerPageReqVO.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/vo/ImFriendManagerRespVO.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/vo/ImFriendRequestManagerPageReqVO.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/vo/ImFriendRequestManagerRespVO.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/ImGroupManagerController.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/ImGroupMemberManagerController.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/ImGroupRequestManagerController.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupManagerBanReqVO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupManagerPageReqVO.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupManagerRespVO.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupRequestManagerPageReqVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupRequestManagerRespVO.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/member/ImGroupMemberManagerRespVO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImChannelMessageManagerController.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImGroupMessageManagerController.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImPrivateMessageManagerController.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessagePageReqVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageRespVO.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageSendReqVO.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/group/ImGroupMessageManagerPageReqVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/group/ImGroupMessageManagerRespVO.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/privates/ImPrivateMessageManagerPageReqVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/privates/ImPrivateMessageManagerRespVO.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/rtc/ImRtcCallManagerController.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/rtc/vo/ImRtcCallManagerPageReqVO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/rtc/vo/ImRtcCallManagerRespVO.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/rtc/vo/ImRtcParticipantManagerRespVO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/ImSensitiveWordManagerController.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordPageReqVO.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordRespVO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordSaveReqVO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/ImStatisticsManagerController.java 160 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerGroupSizeRespVO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerMessageTypeRespVO.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerOverviewRespVO.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerTopSenderRespVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerTrendRespVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImChannelMessageController.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImMessageController.http 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImPrivateMessageController.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/channel/ImChannelMessagePullRespVO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/group/ImGroupMessageListReqVO.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/group/ImGroupMessageRespVO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/group/ImGroupMessageSendReqVO.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessageListReqVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessagePageReqVO.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessageRespVO.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessageSendReqVO.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/ImRtcCallController.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/ImRtcLiveKitController.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/vo/ImRtcCallCreateReqVO.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/vo/ImRtcCallInviteReqVO.java 23 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/vo/ImRtcCallRespVO.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/vo/ImRtcGroupCallRespVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/app/package-info.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/package-info.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelDO.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMaterialDO.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/conversation/ImConversationReadDO.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/face/ImFacePackDO.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/face/ImFacePackItemDO.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/face/ImFaceUserItemDO.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/friend/ImFriendDO.java 90 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/friend/ImFriendRequestDO.java 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupDO.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupMemberDO.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupRequestDO.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImChannelMessageDO.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImGroupMessageDO.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImPrivateMessageDO.java 79 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/AudioMessage.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/CardMessage.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FaceMessage.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FileMessage.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/ImageMessage.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/LocationMessage.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/MaterialMessage.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/MergeMessage.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/QuoteMessage.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/RecallMessage.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/TextMessage.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/VideoMessage.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/rtc/ImRtcCallDO.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/rtc/ImRtcParticipantDO.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/sensitiveword/ImSensitiveWordDO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMaterialMapper.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationReadMapper.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFacePackItemMapper.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFacePackMapper.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFaceUserItemMapper.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendMapper.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendRequestMapper.java 110 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupMapper.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupMemberMapper.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupRequestMapper.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImChannelMessageMapper.java 53 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImGroupMessageMapper.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImPrivateMessageMapper.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/rtc/ImRtcCallMapper.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/rtc/ImRtcParticipantMapper.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/sensitiveword/ImSensitiveWordMapper.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapper.java 191 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/redis/RedisKeyConstants.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/redis/rtc/ImRtcCallLockRedisDAO.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java 108 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ImCommonConstants.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ImContentTypeEnum.java 422 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ImConversationTypeEnum.java 56 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/channel/ImChannelMaterialTypeEnum.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/friend/ImFriendAddSourceEnum.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/friend/ImFriendRequestHandleResultEnum.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/friend/ImFriendStateEnum.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/group/ImGroupAddSourceEnum.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/group/ImGroupMemberRoleEnum.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/group/ImGroupRequestHandleResultEnum.java 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageReceiptStatusEnum.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageStatusEnum.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcCallEndReasonEnum.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcCallMediaTypeEnum.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcCallStatusEnum.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcParticipantRoleEnum.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcParticipantStatusEnum.java 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/config/ImProperties.java 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/package-info.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/config/ImRtcConfiguration.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitClient.java 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitWebhookEventDTO.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/web/config/ImWebConfiguration.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/web/package-info.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/job/rtc/ImRtcCallCleanupJob.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/job/rtc/ImRtcParticipantTimeoutJob.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/mq/consumer/friend/AdminUserProfileUpdateConsumer.java 70 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/package-info.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialService.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialServiceImpl.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelService.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelServiceImpl.java 120 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationReadService.java 67 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationReadServiceImpl.java 86 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackItemService.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackItemServiceImpl.java 117 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackService.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackServiceImpl.java 113 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFaceUserItemService.java 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFaceUserItemServiceImpl.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendRequestService.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendRequestServiceImpl.java 270 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java 129 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java 339 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberService.java 244 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java 323 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestService.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestServiceImpl.java 425 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java 251 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java 773 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageService.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImpl.java 148 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java 548 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageService.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java 297 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/dto/ImGroupMessageSendDTO.java 235 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/dto/ImPrivateMessageSendDTO.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallService.java 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java 1186 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/sensitiveword/ImSensitiveWordService.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/sensitiveword/ImSensitiveWordServiceImpl.java 228 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerService.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImpl.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/ImWebSocketService.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/ImWebSocketServiceImpl.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/ImNotificationWebSocketDTO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/BaseFriendNotification.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendAddNotification.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendBlockNotification.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendDeleteNotification.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendInfoUpdatedNotification.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendRequestApprovedNotification.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendRequestNotification.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendRequestRejectedNotification.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendUnblockNotification.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendUpdateNotification.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/BaseGroupNotification.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupAdminAddNotification.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupAdminRemoveNotification.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupBannedNotification.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupCancelMutedNotification.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupCreateNotification.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupDissolveNotification.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupInfoUpdateNotification.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberCancelMutedNotification.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberEnterNotification.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberInviteNotification.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberKickNotification.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberListNotification.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberMutedNotification.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberNicknameUpdateNotification.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberQuitNotification.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberSettingUpdateNotification.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMessagePinNotification.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMessageUnpinNotification.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMutedNotification.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupNameUpdateNotification.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupNoticeUpdateNotification.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupOwnerTransferNotification.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupRequestApprovedNotification.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupRequestReceivedNotification.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupRequestRejectedNotification.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImChannelMessageNotification.java 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImGroupMessageNotification.java 77 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImMessageReadNotification.java 58 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImMessageReceiptNotification.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImPrivateMessageNotification.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/package-info.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcCallEndNotification.java 76 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcCallNotification.java 165 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcCallStartNotification.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcParticipantConnectedNotification.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcParticipantDisconnectedNotification.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/util/ImMessageUtils.java 218 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationReadMapperTest.java 105 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendRequestMapperTest.java 80 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImGroupMessageMapperTest.java 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImPrivateMessageMapperTest.java 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapperTest.java 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/face/ImFacePackItemServiceImplTest.java 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/face/ImFacePackServiceImplTest.java 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/face/ImFaceUserItemServiceImplTest.java 164 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendRequestServiceImplTest.java 355 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImplTest.java 291 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImplTest.java 349 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestServiceImplTest.java 350 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java 1184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImplTest.java 94 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java 1052 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java 445 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImplTest.java 391 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/sensitiveword/ImSensitiveWordServiceImplTest.java 278 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImplTest.java 162 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/websocket/ImWebSocketServiceImplTest.java 256 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/util/ImMessageUtilsTest.java 184 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/resources/application-unit-test.yaml 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/resources/sql/clean.sql 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-im/src/test/resources/sql/create_tables.sql 349 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileUploadReqVO.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/AppFileController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileMapper.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FilePathUtils.java 106 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java 39 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/components/search-form.vue.vm 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail/index.vue.vm 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/form/index.vue.vm 156 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/index.vue.vm 140 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/api/api.ts.vm 179 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/form.vue.vm 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/import.vue.vm 71 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/index.vue.vm 511 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/form_sub_erp.vue.vm 213 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/form_sub_inner.vue.vm 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/form_sub_normal.vue.vm 347 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/list_sub_erp.vue.vm 424 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/list_sub_inner.vue.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/api/api.ts.vm 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/data.ts.vm 616 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/form.vue.vm 157 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/import.vue.vm 82 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/index.vue.vm 347 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/form_sub_erp.vue.vm 89 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/form_sub_inner.vue.vm 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/form_sub_normal.vue.vm 195 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/list_sub_erp.vue.vm 240 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/list_sub_inner.vue.vm 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java 65 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java 57 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtilsTest.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java 32 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineUniappTest.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_inner/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_normal/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_inner/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_normal/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_inner/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_normal/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_inner/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_normal/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue2_master_inner/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue2_master_normal/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/assert.json 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/ErrorCodeConstants_手动操作 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentController 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentDO 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentMapper 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentPageReqVO 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentRespVO 59 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentSaveReqVO 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentService 62 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentServiceImpl 85 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentServiceImplTest 145 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/sql/h2 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/sql/sql 54 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/ts/index 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/detail/index 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/form/index 187 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/index 169 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/search-form 140 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/xml/InfraStudentMapper 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_master_inner/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-infra/src/test/resources/codegen/vue3_master_normal/java/InfraStudentServiceImpl 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/pom.xml 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java 5 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigRespVO.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigSaveReqVO.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertConfigDO.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataRuleMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimeHelper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherHelper.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java 10 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java 13 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java 252 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml 8 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableFieldTest.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImplTest.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimerConditionIntegrationTest.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleActionTest.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImplTest.java 201 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-core/pom.xml 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusAutoConfiguration.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusProperties.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/kafka/IotKafkaMessageBus.java 127 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/rabbitmq/IotRabbitMQMessageBus.java 104 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/pom.xml 30 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/manager/AbstractIotModbusPollScheduler.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/utils/IotModbusTcpClientUtils.java 37 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/manager/IotModbusTcpClientPollScheduler.java 219 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/IotModbusTcpServerProtocol.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/manager/IotModbusTcpServerConfigCacheService.java 24 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/manager/IotModbusTcpServerConnectionManager.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java 13 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/manager/IotModbusTcpClientPollSchedulerTest.java 147 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java 11 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java 17 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImplTest.java 325 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImplTest.java 116 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/test/resources/sql/clean.sql 3 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-promotion/src/test/resources/sql/create_tables.sql 60 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryPickUpStoreController.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryPickUpStoreConvert.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImpl.java 31 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml 27 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/test/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImplTest.java 200 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mall/yudao-module-trade/src/test/resources/sql/create_tables.sql 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm-api/pom.xml 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/pom.xml 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/brand/MdmBrandApi.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/brand/MdmBrandApiImpl.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/brand/dto/MdmBrandRespDTO.java 22 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApi.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApiImpl.java 49 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/dto/MdmItemCategoryRespDTO.java 28 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/MdmItemApi.java 143 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/MdmItemApiImpl.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/MdmItemSkuApi.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/MdmItemSkuApiImpl.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemCreateReqDTO.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemRespDTO.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemSkuRespDTO.java 51 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemUpdateReqDTO.java 66 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/unit/MdmUnitMeasureApi.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/unit/MdmUnitMeasureApiImpl.java 55 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/unit/dto/MdmUnitMeasureRespDTO.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/warehouse/MdmWarehouseApi.java 72 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/warehouse/MdmWarehouseApiImpl.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/warehouse/dto/MdmWarehouseRespDTO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/brand/MdmBrandController.java 87 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/brand/vo/MdmBrandPageReqVO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/brand/vo/MdmBrandSaveReqVO.java 25 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/category/MdmItemCategoryController.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/category/vo/MdmItemCategoryPageReqVO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/category/vo/MdmItemCategoryRespVO.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/category/vo/MdmItemCategorySaveReqVO.java 34 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/MdmItemController.java 93 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemPageReqVO.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemRespVO.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemSaveReqVO.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/unit/MdmUnitMeasureController.java 88 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/unit/vo/MdmUnitMeasurePageReqVO.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/unit/vo/MdmUnitMeasureSaveReqVO.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/warehouse/MdmWarehouseController.java 98 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/warehouse/vo/MdmWarehousePageReqVO.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/warehouse/vo/MdmWarehouseRespVO.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/warehouse/vo/MdmWarehouseSaveReqVO.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/brand/MdmBrandDO.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/category/MdmItemCategoryDO.java 73 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/item/MdmItemBatchConfigDO.java 107 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/item/MdmItemDO.java 177 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/item/MdmItemSkuDO.java 99 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/unit/MdmUnitMeasureDO.java 69 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/warehouse/MdmWarehouseDO.java 84 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/brand/MdmBrandMapper.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/category/MdmItemCategoryMapper.java 63 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/item/MdmItemBatchConfigMapper.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/item/MdmItemMapper.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/item/MdmItemSkuMapper.java 45 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/unit/MdmUnitMeasureMapper.java 50 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/warehouse/MdmWarehouseMapper.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/enums/ErrorCodeConstants.java 46 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/enums/MdmItemTypeEnum.java 42 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/brand/MdmBrandService.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/brand/MdmBrandServiceImpl.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryService.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryServiceImpl.java 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemService.java 134 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java 189 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemSkuService.java 130 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemSkuServiceImpl.java 151 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/unit/MdmUnitMeasureService.java 102 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/unit/MdmUnitMeasureServiceImpl.java 124 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/warehouse/MdmWarehouseService.java 97 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/warehouse/MdmWarehouseServiceImpl.java 126 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/vo/AddressRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java 9 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-member/src/test/resources/sql/create_tables.sql 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/pom.xml 14 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/andon/MesProAndonConfigController.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/andon/vo/config/MesProAndonConfigRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/defectrecord/MesQcDefectRecordController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/miscreceipt/MesWmMiscReceiptController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/miscreceipt/MesWmMiscReceiptLineController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueDetailController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueLineController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/packages/MesWmPackageController.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/packages/MesWmPackageLineController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesDetailController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesLineController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/sn/MesWmSnController.java 21 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderDO.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/template/MesQcTemplateMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/barcode/MesWmBarcodeMapper.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkOrderStatusEnum.java 18 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/BarcodeBizTypeEnum.java 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImpl.java 47 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java 50 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderService.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java 75 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/listener/MesProWorkOrderStatusListener.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/defectrecord/MesQcDefectRecordService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/defectrecord/MesQcDefectRecordServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeServiceImpl.java 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/sn/MesWmSnServiceImpl.java 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImplTest.java 36 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/service/handler/user/SubscribeHandler.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/PayChannelController.java 9 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletController.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/convert/channel/PayChannelConvert.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/channel/PayChannelMapper.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java 26 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/test/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceTest.java 91 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-pay/src/test/resources/sql/create_tables.sql 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/message/user/AdminUserProfileUpdateMessage.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApi.java 32 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApiImpl.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/dto/RoleRespDTO.java 37 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSimpleRespVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSimpleRespVO.java 19 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java 44 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/social/config/YudaoWxClientConfiguration.java 103 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/weixin/config/YudaoWxClientConfiguration.java 111 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/weixin/package-info.java 6 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/user/AdminUserProducer.java 33 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java 15 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java 17 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java 2 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/pom.xml 7 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/WmsInventoryApi.java 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/WmsInventoryApiImpl.java 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsInventoryQueryReqDTO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsInventoryRespDTO.java 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsReceiptReqDTO.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsReceiptResultDTO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsReserveReqDTO.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsShipmentReqDTO.java 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsShipmentResultDTO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsUseReserveReqDTO.java 30 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/stock/WmsStockApi.java 144 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/stock/dto/StockRecordCreateDTO.java 81 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryReserveController.java 137 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryReservePageReqVO.java 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryReserveRespVO.java 64 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemListReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemPageReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemSaveReqVO.java 3 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryReserveDO.java 100 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryTransactionDO.java 116 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemSkuDO.java 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryReserveMapper.java 52 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryTransactionMapper.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/DictTypeConstants.java 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java 8 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsInventoryBizTypeEnum.java 68 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsReserveBizTypeEnum.java 39 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsReserveStatusEnum.java 41 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsTransactionTypeEnum.java 43 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/stock/StockBizTypeEnum.java 119 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryReserveService.java 74 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryReserveServiceImpl.java 210 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java 20 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryTransactionService.java 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryTransactionServiceImpl.java 38 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemServiceImpl.java 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java 31 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java 40 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/pom.xml 24 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-dev.yaml 16 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-local.yaml 22 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application-test.yaml 284 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
yudao-server/src/main/resources/application.yaml 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
.gitignore
@@ -51,4 +51,5 @@
application-my.yaml
/yudao-ui-app/unpackage/
**/.DS_Store
.DS_Store
**/.DS_Store
.image/common/im-feature.png
.image/common/im-preview-home.png
.image/common/im-preview-manager.png
README.md
@@ -1,5 +1,5 @@
<p align="center">
 <img src="https://img.shields.io/badge/Spring%20Boot-3.4.5-blue.svg" alt="Downloads">
 <img src="https://img.shields.io/badge/Spring%20Boot-4.1.0-blue.svg" alt="Downloads">
 <img src="https://img.shields.io/badge/Vue-3.2-blue.svg" alt="Downloads">
 <img src="https://img.shields.io/github/license/YunaiV/ruoyi-vue-pro" alt="Downloads" />
</p>
@@ -23,13 +23,13 @@
## ðŸ° ç‰ˆæœ¬è¯´æ˜Ž
| ç‰ˆæœ¬                                                                  | JDK 8 + Spring Boot 2.7                                                   | JDK 17/21 + Spring Boot 3.2                                                           |
|---------------------------------------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| ã€å®Œæ•´ç‰ˆã€‘[ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/) åˆ†æ”¯ | [`master-jdk17`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-jdk17/) åˆ†æ”¯ |
| ã€ç²¾ç®€ç‰ˆã€‘[yudao-boot-mini](https://gitee.com/yudaocode/yudao-boot-mini) | [`master`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master/) åˆ†æ”¯   | [`master-jdk17`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master-jdk17/) åˆ†æ”¯   |
| ç‰ˆæœ¬                                                                  | JDK 8 + Spring Boot 2.7                                                   | JDK 17/21 + Spring Boot 3.5                                                           | JDK 25 + Spring Boot 4.x                                                              |
|---------------------------------------------------------------------|---------------------------------------------------------------------------|---------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------|
| ã€å®Œæ•´ç‰ˆã€‘[ruoyi-vue-pro](https://gitee.com/zhijiantianya/ruoyi-vue-pro) | [`master`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master/) åˆ†æ”¯ | [`master-jdk17`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-jdk17/) åˆ†æ”¯ | [`master-jdk25`](https://gitee.com/zhijiantianya/ruoyi-vue-pro/tree/master-jdk25/) åˆ†æ”¯ |
| ã€ç²¾ç®€ç‰ˆã€‘[yudao-boot-mini](https://gitee.com/yudaocode/yudao-boot-mini) | [`master`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master/) åˆ†æ”¯   | [`master-jdk17`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master-jdk17/) åˆ†æ”¯   | [`master-jdk25`](https://gitee.com/yudaocode/yudao-boot-mini/tree/master-jdk25/) åˆ†æ”¯   |
* ã€å®Œæ•´ç‰ˆã€‘:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、AI å¤§æ¨¡åž‹ã€IoT ç‰©è”网 ç­‰åŠŸèƒ½
* ã€ç²¾ç®€ç‰ˆã€‘:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、AI å¤§æ¨¡åž‹ã€IoT ç‰©è”网 ç­‰åŠŸèƒ½
* ã€å®Œæ•´ç‰ˆã€‘:包括系统功能、基础设施、会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、IM å³æ—¶é€šè®¯ã€AI å¤§æ¨¡åž‹ã€IoT ç‰©è”网等功能
* ã€ç²¾ç®€ç‰ˆã€‘:只包括系统功能、基础设施功能,不包括会员中心、数据报表、工作流程、商城系统、微信公众号、CRM、ERP、WMS、MES、IM å³æ—¶é€šè®¯ã€AI å¤§æ¨¡åž‹ã€IoT ç‰©è”网等功能
可参考 [《迁移文档》](https://doc.iocoder.cn/migrate-module/) ï¼Œåªéœ€è¦ 5-10 åˆ†é’Ÿï¼Œå³å¯å°†ã€å®Œæ•´ç‰ˆã€‘按需迁移到【精简版】
@@ -43,7 +43,7 @@
![架构图](/.image/common/ruoyi-vue-pro-architecture.png)
* Java åŽç«¯ï¼š`master` åˆ†æ”¯ä¸º JDK 8 + Spring Boot 2.7,`master-jdk17` åˆ†æ”¯ä¸º JDK 17/21 + Spring Boot 3.2
* Java åŽç«¯ï¼š`master` åˆ†æ”¯ä¸º JDK 8 + Spring Boot 2.7,`master-jdk17` åˆ†æ”¯ä¸º JDK 17/21 + Spring Boot 3.5,`master-jdk25` åˆ†æ”¯ä¸º JDK 25 + Spring Boot 4.x
* ç®¡ç†åŽå°çš„电脑端:Vue3 æä¾› `element-plus`、`vben(ant-design-vue)` ä¸¤ä¸ªç‰ˆæœ¬ï¼ŒVue2 æä¾› `element-ui` ç‰ˆæœ¬
* ç®¡ç†åŽå°çš„移动端:采用 `uni-app` æ–¹æ¡ˆï¼Œä¸€ä»½ä»£ç å¤šç»ˆç«¯é€‚配,同时支持 APP、小程序、H5!
* åŽç«¯é‡‡ç”¨ Spring Boot å¤šæ¨¡å—架构、MySQL + MyBatis Plus、Redis + Redisson
@@ -102,7 +102,7 @@
团队包含专业的项目经理、架构师、前端工程师、后端工程师、测试工程师、运维工程师,可以提供全流程的外包服务。
项目可以是商城、SCRM ç³»ç»Ÿã€OA ç³»ç»Ÿã€ç‰©æµç³»ç»Ÿã€ERP ç³»ç»Ÿã€CMS ç³»ç»Ÿã€HIS ç³»ç»Ÿã€æ”¯ä»˜ç³»ç»Ÿã€IM èŠå¤©ã€å¾®ä¿¡å…¬ä¼—号、微信小程序等等。
项目可以是商城、SCRM ç³»ç»Ÿã€OA ç³»ç»Ÿã€ç‰©æµç³»ç»Ÿã€ERP ç³»ç»Ÿã€CMS ç³»ç»Ÿã€HIS ç³»ç»Ÿã€æ”¯ä»˜ç³»ç»Ÿã€IM å³æ—¶é€šè®¯ã€å¾®ä¿¡å…¬ä¼—号、微信小程序等等。
## ðŸ¼ å†…置功能
@@ -308,6 +308,19 @@
![预览图](/.image/common/iot-preview.png)
### IM å³æ—¶é€šè®¯
演示地址(Boot):<https://doc.iocoder.cn/im-preview/>
演示地址(Vue3 + Element Plus):<http://dashboard-vue3.yudao.iocoder.cn>
![功能图](/.image/common/im-feature.png)
| èŠå¤©ç•Œé¢ | èŠå¤©ç®¡ç† |
| --- | --- |
| ![聊天界面](/.image/common/im-preview-home.png) | ![聊天管理](/.image/common/im-preview-manager.png) |
## ðŸ¨ æŠ€æœ¯æ ˆ
### æ¨¡å—
@@ -327,6 +340,7 @@
| `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 æ¨¡å—   |
@@ -336,42 +350,42 @@
| æ¡†æž¶                                                                                          | è¯´æ˜Ž               | ç‰ˆæœ¬             | å­¦ä¹ æŒ‡å—                                                           |
|---------------------------------------------------------------------------------------------|------------------|----------------|----------------------------------------------------------------|
| [Spring Boot](https://spring.io/projects/spring-boot)                                       | åº”用开发框架           | 3.5.5          | [文档](https://github.com/YunaiV/SpringBoot-Labs)                |
| [Spring Boot](https://spring.io/projects/spring-boot)                                       | åº”用开发框架           | 4.1.0          | [文档](https://github.com/YunaiV/SpringBoot-Labs)                |
| [MySQL](https://www.mysql.com/cn/)                                                          | æ•°æ®åº“服务器           | 5.7 / 8.0+     |                                                                |
| [Druid](https://github.com/alibaba/druid)                                                   | JDBC è¿žæŽ¥æ± ã€ç›‘控组件    | 1.2.27         | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/)                                                    | MyBatis å¢žå¼ºå·¥å…·åŒ…    | 3.5.12         | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao)         |
| [Dynamic Datasource](https://dynamic-datasource.com/)                                       | åŠ¨æ€æ•°æ®æº            | 4.3.1          | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [Druid](https://github.com/alibaba/druid)                                                   | JDBC è¿žæŽ¥æ± ã€ç›‘控组件    | 1.2.28         | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [MyBatis Plus](https://mp.baomidou.com/)                                                    | MyBatis å¢žå¼ºå·¥å…·åŒ…    | 3.5.16         | [文档](http://www.iocoder.cn/Spring-Boot/MyBatis/?yudao)         |
| [Dynamic Datasource](https://dynamic-datasource.com/)                                       | åŠ¨æ€æ•°æ®æº            | 4.5.0          | [文档](http://www.iocoder.cn/Spring-Boot/datasource-pool/?yudao) |
| [Redis](https://redis.io/)                                                                  | key-value æ•°æ®åº“    | 5.0 / 6.0 /7.0 |                                                                |
| [Redisson](https://github.com/redisson/redisson)                                            | Redis å®¢æˆ·ç«¯        | 3.35.0         | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao)           |
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC æ¡†æž¶           | 6.2.9          | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao)               |
| [Spring Security](https://github.com/spring-projects/spring-security)                       | Spring å®‰å…¨æ¡†æž¶      | 6.5.2          | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator)                     | å‚数校验组件           | 8.0.2          | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao)      |
| [Flowable](https://github.com/flowable/flowable-engine)                                     | å·¥ä½œæµå¼•擎            | 7.0.0          | [文档](https://doc.iocoder.cn/bpm/)                              |
| [Quartz](https://github.com/quartz-scheduler)                                               | ä»»åŠ¡è°ƒåº¦ç»„ä»¶           | 2.5.0          | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao)             |
| [Springdoc](https://springdoc.org/)                                                         | Swagger æ–‡æ¡£       | 2.8.9          | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao)         |
| [SkyWalking](https://skywalking.apache.org/)                                                | åˆ†å¸ƒå¼åº”用追踪系统        | 9.5.0          | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao)      |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin)                       | Spring Boot ç›‘控平台 | 3.5.2          | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao)           |
| [Jackson](https://github.com/FasterXML/jackson)                                             | JSON å·¥å…·åº“         | 2.30.14        |                                                                |
| [Redisson](https://github.com/redisson/redisson)                                            | Redis å®¢æˆ·ç«¯        | 4.6.1          | [文档](http://www.iocoder.cn/Spring-Boot/Redis/?yudao)           |
| [Spring MVC](https://github.com/spring-projects/spring-framework/tree/master/spring-webmvc) | MVC æ¡†æž¶           | 7.0.8          | [文档](http://www.iocoder.cn/SpringMVC/MVC/?yudao)               |
| [Spring Security](https://github.com/spring-projects/spring-security)                       | Spring å®‰å…¨æ¡†æž¶      | 7.1.0          | [文档](http://www.iocoder.cn/Spring-Boot/Spring-Security/?yudao) |
| [Hibernate Validator](https://github.com/hibernate/hibernate-validator)                     | å‚数校验组件           | 9.1.0          | [文档](http://www.iocoder.cn/Spring-Boot/Validation/?yudao)      |
| [Flowable](https://github.com/flowable/flowable-engine)                                     | å·¥ä½œæµå¼•擎            | 8.0.0          | [文档](https://doc.iocoder.cn/bpm/)                              |
| [Quartz](https://github.com/quartz-scheduler)                                               | ä»»åŠ¡è°ƒåº¦ç»„ä»¶           | 2.5.2          | [文档](http://www.iocoder.cn/Spring-Boot/Job/?yudao)             |
| [Springdoc](https://springdoc.org/)                                                         | Swagger æ–‡æ¡£       | 3.0.3          | [文档](http://www.iocoder.cn/Spring-Boot/Swagger/?yudao)         |
| [SkyWalking](https://skywalking.apache.org/)                                                | åˆ†å¸ƒå¼åº”用追踪系统        | 9.6.0          | [文档](http://www.iocoder.cn/Spring-Boot/SkyWalking/?yudao)      |
| [Spring Boot Admin](https://github.com/codecentric/spring-boot-admin)                       | Spring Boot ç›‘控平台 | 4.0.4          | [文档](http://www.iocoder.cn/Spring-Boot/Admin/?yudao)           |
| [Jackson](https://github.com/FasterXML/jackson)                                             | JSON å·¥å…·åº“         | 3.1.4          |                                                                |
| [MapStruct](https://mapstruct.org/)                                                         | Java Bean è½¬æ¢     | 1.6.3          | [文档](http://www.iocoder.cn/Spring-Boot/MapStruct/?yudao)       |
| [Lombok](https://projectlombok.org/)                                                        | æ¶ˆé™¤å†—é•¿çš„ Java ä»£ç     | 1.18.38        | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao)          |
| [JUnit](https://junit.org/junit5/)                                                          | Java å•元测试框架      | 5.12.2         | -                                                              |
| [Mockito](https://github.com/mockito/mockito)                                               | Java Mock æ¡†æž¶     | 5.17.0         | -                                                              |
| [Lombok](https://projectlombok.org/)                                                        | æ¶ˆé™¤å†—é•¿çš„ Java ä»£ç     | 1.18.46        | [文档](http://www.iocoder.cn/Spring-Boot/Lombok/?yudao)          |
| [JUnit](https://junit.org/junit5/)                                                          | Java å•元测试框架      | 6.0.3          | -                                                              |
| [Mockito](https://github.com/mockito/mockito)                                               | Java Mock æ¡†æž¶     | 5.23.0         | -                                                              |
## ðŸ· æ¼”示图
### ç³»ç»ŸåŠŸèƒ½
| æ¨¡å—       | biu                         | biu                       | biu                      |
|----------|-----------------------------|---------------------------|--------------------------|
| æ¨¡å—       | biu                         | biu                       | biu                       |
|----------|-----------------------------|---------------------------|---------------------------|
| ç™»å½• & é¦–页  | ![登录](/.image/登录.jpg)       | ![首页](/.image/首页.jpg)     | ![个人中心](/.image/个人中心.jpg) |
| ç”¨æˆ· & åº”用  | ![用户管理](/.image/用户管理.jpg)   | ![令牌管理](/.image/令牌管理.jpg) | ![应用管理](/.image/应用管理.jpg) |
| ç§Ÿæˆ· & å¥—餐  | ![租户管理](/.image/租户管理.jpg)   | ![租户套餐](/.image/租户套餐.png) | -                        |
| éƒ¨é—¨ & å²—位  | ![部门管理](/.image/部门管理.jpg)   | ![岗位管理](/.image/岗位管理.jpg) | -                        |
| èœå• & è§’色  | ![菜单管理](/.image/菜单管理.jpg)   | ![角色管理](/.image/角色管理.jpg) | -                        |
| å®¡è®¡æ—¥å¿—     | ![操作日志](/.image/操作日志.jpg)   | ![登录日志](/.image/登录日志.jpg) | -                        |
| ç§Ÿæˆ· & å¥—餐  | ![租户管理](/.image/租户管理.jpg)   | ![租户套餐](/.image/租户套餐.png) | -                         |
| éƒ¨é—¨ & å²—位  | ![部门管理](/.image/部门管理.jpg)   | ![岗位管理](/.image/岗位管理.jpg) | -                         |
| èœå• & è§’色  | ![菜单管理](/.image/菜单管理.jpg)   | ![角色管理](/.image/角色管理.jpg) | -                         |
| å®¡è®¡æ—¥å¿—     | ![操作日志](/.image/操作日志.jpg)   | ![登录日志](/.image/登录日志.jpg) | -                         |
| çŸ­ä¿¡       | ![短信渠道](/.image/短信渠道.jpg)   | ![短信模板](/.image/短信模板.jpg) | ![短信日志](/.image/短信日志.jpg) |
| å­—å…¸ & æ•æ„Ÿè¯ | ![字典类型](/.image/字典类型.jpg)   | ![字典数据](/.image/字典数据.jpg) | ![敏感词](/.image/敏感词.jpg)  |
| é”™è¯¯ç  & é€šçŸ¥ | ![错误码管理](/.image/错误码管理.jpg) | ![通知公告](/.image/通知公告.jpg) | -                        |
| å­—å…¸ & æ•æ„Ÿè¯ | ![字典类型](/.image/字典类型.jpg)   | ![字典数据](/.image/字典数据.jpg) | ![敏感词](/.image/敏感词.jpg)   |
| é”™è¯¯ç  & é€šçŸ¥ | ![错误码管理](/.image/错误码管理.jpg) | ![通知公告](/.image/通知公告.jpg) | -                         |
### å·¥ä½œæµç¨‹
docs/123.png
Binary files differ
docs/batch1/database_changes.sql
ÎļþÒÑɾ³ý
docs/batch1/frontend_changes.md
ÎļþÒÑɾ³ý
docs/batch2/database_changes.sql
ÎļþÒÑɾ³ý
docs/batch2/frontend_changes.md
ÎļþÒÑɾ³ý
docs/batch3/database_changes.sql
ÎļþÒÑɾ³ý
docs/batch3/frontend_changes.md
ÎļþÒÑɾ³ý
docs/batch4/database_changes.sql
ÎļþÒÑɾ³ý
docs/batch4/frontend_changes.md
ÎļþÒÑɾ³ý
docs/batch5/database_changes.sql
ÎļþÒÑɾ³ý
docs/batch5/frontend_changes.md
ÎļþÒÑɾ³ý
docs/batch6/database_changes.sql
ÎļþÒÑɾ³ý
docs/batch6/frontend_changes.md
ÎļþÒÑɾ³ý
docs/cross_module_integration_plan.md
ÎļþÒÑɾ³ý
docs/database_schema.sql
ÎļþÒÑɾ³ý
docs/im_module.sql
ÎļþÒÑɾ³ý
docs/im_module_unlock.md
ÎļþÒÑɾ³ý
docs/mdm_frontend_change.md
ÎļþÒÑɾ³ý
docs/mes_wm_schema.sql
ÎļþÒÑɾ³ý
docs/workflow_guide.md
ÎļþÒÑɾ³ý
pom.xml
@@ -17,19 +17,18 @@
        <module>yudao-module-infra</module>
        <module>yudao-module-member</module>
        <module>yudao-module-bpm</module>
        <module>yudao-module-report</module>
        <module>yudao-module-mp</module>
        <module>yudao-module-pay</module>
        <module>yudao-module-mall</module>
<!--        <module>yudao-module-report</module>-->
<!--        <module>yudao-module-mp</module>-->
<!--        <module>yudao-module-pay</module>-->
<!--        <module>yudao-module-mall</module>-->
        <module>yudao-module-crm</module>
        <module>yudao-module-erp</module>
        <module>yudao-module-iot</module>
<!--        <module>yudao-module-iot</module>-->
        <module>yudao-module-mes</module>
        <module>yudao-module-wms</module>
        <module>yudao-module-mdm</module>
        <module>yudao-module-mdm-api</module>
<!-- è¯·å‚考 https://doc.iocoder.cn/ai/build/ æ–‡æ¡£ï¼Œå®Œæˆ AI æ¨¡å—的启动!!! -->
        <module>yudao-module-ai</module>
        <module>yudao-module-im</module>
        <!-- è¯·å‚考 https://doc.iocoder.cn/ai/build/ æ–‡æ¡£ï¼Œå®Œæˆ AI æ¨¡å—的启动!!! -->
<!--        <module>yudao-module-ai</module>-->
    </modules>
    <name>${project.artifactId}</name>
@@ -37,9 +36,9 @@
    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
    <properties>
        <revision>2026.04-SNAPSHOT</revision>
        <revision>2026.06-jdk25-SNAPSHOT</revision>
        <!-- Maven ç›¸å…³ -->
        <java.version>21</java.version>
        <java.version>25</java.version>
        <maven.compiler.source>${java.version}</maven.compiler.source>
        <maven.compiler.target>${java.version}</maven.compiler.target>
        <maven-surefire-plugin.version>3.5.3</maven-surefire-plugin.version>
@@ -47,7 +46,7 @@
        <flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
        <!-- maven-surefire-plugin æš‚时无法通过 bom çš„依赖读取(兼容老版本 IDEA 2024 åŠä»¥å‰ç‰ˆæœ¬ï¼‰ -->
        <lombok.version>1.18.46</lombok.version>
        <spring.boot.version>4.0.6</spring.boot.version>
        <spring.boot.version>4.1.0</spring.boot.version>
        <mapstruct.version>1.6.3</mapstruct.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
@@ -94,7 +93,7 @@
                            </path>
                            <path>
                                <!-- ç¡®ä¿ Lombok ç”Ÿæˆçš„ getter/setter æ–¹æ³•能被 MapStruct æ­£ç¡®è¯†åˆ«ï¼Œ
                                     é¿å…å‡ºçް No property named â€œxxx" exists çš„编译错误 -->
                                     é¿å…å‡ºçް No property named "xxx" exists çš„编译错误 -->
                                <groupId>org.projectlombok</groupId>
                                <artifactId>lombok-mapstruct-binding</artifactId>
                                <version>0.2.0</version>
@@ -151,14 +150,6 @@
    <!-- ä½¿ç”¨ huawei / aliyun çš„ Maven æºï¼Œæå‡ä¸‹è½½é€Ÿåº¦ -->
    <repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>huaweicloud</id>
            <name>huawei</name>
script/livekit-poc/README.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
# LiveKit Server PoC
最小可用的 LiveKit Server è‡ªéƒ¨ç½²éªŒè¯çŽ¯å¢ƒï¼Œç”¨äºŽé›¶æœŸ PoC。
## å¯åЍ
```bash
cd tools/livekit-poc
docker compose up -d
bash verify.sh
```
## ç«¯å£
- 7880:HTTP / WebSocket ä¿¡ä»¤ï¼›
- 7881:WebRTC TCP fallback;
- 7882/UDP:WebRTC åª’体;
- macOS / Windows:当前 `docker-compose.yml` èµ°ç«¯å£æ˜ å°„模式,webhook URL ç”¨ `host.docker.internal:48080` è®©å®¹å™¨è®¿é—®åˆ°å®¿ä¸»æœº yudao åŽç«¯ï¼›
- macOS ä¸Š host network(`network_mode: host`)需要 Docker Desktop 4.34+ å¹¶åœ¨ Settings â†’ Resources â†’ Network å‹¾é€‰ã€ŒEnable host networking」,老版本静默失败(容器跑得起来但端口完全不通);
- Linux:可以把 `docker-compose.yml` æ”¹æˆ `network_mode: host` + åˆ  `ports:` æ®µï¼Œå¹¶æŠŠ `livekit.yaml` çš„ webhook URL æ”¹ä¸º `http://127.0.0.1:48080/admin-api/im/livekit/webhook`。
## å‡­æ® (仅 PoC,勿用于生产)
- `LIVEKIT_KEYS=devkey: secret-poc-key-min-32-chars-required-here`
- API Key:`devkey`
- API Secret:`secret-poc-key-min-32-chars-required-here`
生产环境必须改用强随机 secret,并通过 `--config /etc/livekit.yaml` åŠ è½½ã€‚
## æµè§ˆå™¨è”è°ƒ
`verify.sh` è·‘完会输出一个 `meet.livekit.io` é“¾æŽ¥ï¼Œç”¨ä¸¤ä¸ªæµè§ˆå™¨ï¼ˆæˆ–两台机器)打开同一链接即可看到对方画面。
script/livekit-poc/docker-compose.yml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
services:
  livekit:
    image: docker.m.daocloud.io/livekit/livekit-server:latest
    container_name: yudao-livekit-dev
    restart: unless-stopped
    # ç«¯å£æ˜ å°„模式
    # macOS / Windows å¿…走这种方式:Docker Desktop 4.34 ä»¥ä¸‹æ²¡æœ‰ host network
    # Linux å¯ä»¥æ”¹ network_mode: host çœåŽ»æ˜ å°„ï¼Œå¹¶æŠŠ livekit.yaml çš„ webhook url æ¢æˆ 127.0.0.1
    ports:
      - "7880:7880"          # HTTP / WebSocket ä¿¡ä»¤
      - "7881:7881"          # WebRTC TCP fallback
      - "7882:7882/udp"      # WebRTC UDP (dev æ¨¡å¼ UDP mux å•端口)
    volumes:
      # æŒ‚è½½ config æ–‡ä»¶ï¼›webhook é…ç½®åœ¨ livekit.yaml é‡Œ
      - ./livekit.yaml:/etc/livekit.yaml:ro
    command:
      - --config
      - /etc/livekit.yaml
      - --bind
      - 0.0.0.0
script/livekit-poc/livekit.yaml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
# LiveKit Server æœ¬åœ°å¼€å‘配置(PoC ç”¨ï¼Œå‹¿ç”¨äºŽç”Ÿäº§ï¼‰
# æ›¿ä»£ docker --dev æ¨¡å¼ï¼›ä¸ºæ”¯æŒ webhook å¿…须用 config æ–‡ä»¶è€Œéž env
keys:
  devkey: secret-poc-key-min-32-chars-required-here
# ç«¯å£
port: 7880
rtc:
  tcp_port: 7881
  udp_port: 7882
  use_external_ip: false
# Webhook:成员离开 / æˆ¿é—´ç»“束等事件回调到 yudao åŽç«¯åšä¸šåŠ¡æ€å…œåº•æ¸…ç†
# host.docker.internal è®©å®¹å™¨è®¿é—®å®¿ä¸»æœº macOS / Windows ä¸Šçš„ yudao åŽç«¯
# Linux ä¸Š docker compose å¯æ”¹ network_mode: host,这里同步改成 127.0.0.1
# api_key ç”¨äºŽç­¾å‘ JWT,yudao åŽç«¯ç”¨ç›¸åŒ secret éªŒè¯ç­¾å
webhook:
  api_key: devkey
  urls:
    - http://host.docker.internal:48080/admin-api/im/livekit/webhook
# æˆ¿é—´æ— äººæ—¶å¤šä¹…销毁;秒;之前 PoC é»˜è®¤ 300,给低些方便排查
room:
  empty_timeout: 300
  departure_timeout: 20
# å¼€å‘模式:放宽 secret é•¿åº¦é™åˆ¶ + å†…ç½® TURN æœåŠ¡
development: true
log_level: info
script/livekit-poc/verify.sh
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,105 @@
#!/usr/bin/env bash
# LiveKit Server PoC éªŒè¯è„šæœ¬ï¼›
# ç”¨æ³•: bash verify.sh
set -e
API_KEY="${LIVEKIT_API_KEY:-devkey}"
API_SECRET="${LIVEKIT_API_SECRET:-secret-poc-key-min-32-chars-required-here}"
HOST="${LIVEKIT_HOST:-localhost:7880}"
ROOM="${LIVEKIT_ROOM:-poc-room}"
ok()   { printf "[OK]   %s\n" "$1"; }
fail() { printf "[FAIL] %s\n" "$1"; exit 1; }
echo "==> 1/5 ç­‰å¾… HTTP ç«¯ç‚¹å°±ç»ª (http://${HOST}/)"
for i in $(seq 1 20); do
  code=$(curl -s -o /dev/null -w "%{http_code}" "http://${HOST}/" || echo "000")
  [ "$code" = "200" ] && { ok "HTTP 200"; break; }
  [ $i -eq 20 ] && fail "20 ç§’内未就绪 (last code=${code})"
  sleep 1
done
echo "==> 2/5 ç­¾å‘管理 + å®¢æˆ·ç«¯æƒé™ Token"
TOKEN=$(API_KEY="$API_KEY" API_SECRET="$API_SECRET" ROOM="$ROOM" python3 - <<'PY'
import json, time, hmac, hashlib, base64, os
def b64u(b): return base64.urlsafe_b64encode(b).rstrip(b'=').decode()
header  = b64u(json.dumps({"alg":"HS256","typ":"JWT"}, separators=(',',':')).encode())
payload = b64u(json.dumps({
    "iss": os.environ["API_KEY"],
    "sub": "poc-tester",
    "name": "PoC Tester",
    "video": {
        "roomJoin": True, "room": os.environ["ROOM"],
        "canPublish": True, "canSubscribe": True, "canPublishData": True,
        "roomCreate": True, "roomList": True, "roomAdmin": True
    },
    "exp": int(time.time()) + 3600,
    "nbf": int(time.time())
}, separators=(',',':')).encode())
sig = b64u(hmac.new(os.environ["API_SECRET"].encode(),
                    f"{header}.{payload}".encode(),
                    hashlib.sha256).digest())
print(f"{header}.{payload}.{sig}")
PY
)
[ -n "$TOKEN" ] || fail "Token ç”Ÿæˆå¤±è´¥"
ok "Token å·²ç”Ÿæˆ (${#TOKEN} chars)"
echo "==> 3/5 åˆ›å»ºæˆ¿é—´ ${ROOM} (CreateRoom RPC)"
create_resp=$(curl -s -X POST "http://${HOST}/twirp/livekit.RoomService/CreateRoom" \
    -H "Authorization: Bearer ${TOKEN}" \
    -H "Content-Type: application/json" \
    -d "{\"name\":\"${ROOM}\",\"empty_timeout\":300,\"max_participants\":10}")
echo "    å“åº”: $create_resp"
echo "$create_resp" | jq -e '.sid' >/dev/null 2>&1 \
    && ok "房间已创建" \
    || fail "CreateRoom å¤±è´¥"
echo "==> 4/5 åˆ—出房间 (ListRooms RPC)"
list_resp=$(curl -s -X POST "http://${HOST}/twirp/livekit.RoomService/ListRooms" \
    -H "Authorization: Bearer ${TOKEN}" \
    -H "Content-Type: application/json" \
    -d '{}')
room_count=$(echo "$list_resp" | jq '.rooms | length' 2>/dev/null || echo "0")
ok "当前房间数: ${room_count}"
echo "$list_resp" | jq '.'
echo "==> 5/5 åˆ é™¤æˆ¿é—´ (DeleteRoom RPC) â€”— æ¸…理"
del_resp=$(curl -s -X POST "http://${HOST}/twirp/livekit.RoomService/DeleteRoom" \
    -H "Authorization: Bearer ${TOKEN}" \
    -H "Content-Type: application/json" \
    -d "{\"room\":\"${ROOM}\"}")
ok "删除响应: $del_resp"
# é‡æ–°ç­¾ä¸€ä¸ªä»… client æƒé™çš„ token;用于浏览器进会
CLIENT_TOKEN=$(API_KEY="$API_KEY" API_SECRET="$API_SECRET" ROOM="$ROOM" python3 - <<'PY'
import json, time, hmac, hashlib, base64, os
def b64u(b): return base64.urlsafe_b64encode(b).rstrip(b'=').decode()
header  = b64u(json.dumps({"alg":"HS256","typ":"JWT"}, separators=(',',':')).encode())
payload = b64u(json.dumps({
    "iss": os.environ["API_KEY"],
    "sub": "browser-tester",
    "name": "Browser",
    "video": {
        "roomJoin": True, "room": os.environ["ROOM"],
        "canPublish": True, "canSubscribe": True, "canPublishData": True
    },
    "exp": int(time.time()) + 7200
}, separators=(',',':')).encode())
sig = b64u(hmac.new(os.environ["API_SECRET"].encode(),
                    f"{header}.{payload}".encode(),
                    hashlib.sha256).digest())
print(f"{header}.{payload}.{sig}")
PY
)
echo ""
echo "============================================================"
echo "  LiveKit Server éªŒè¯é€šè¿‡"
echo "============================================================"
echo "  æµè§ˆå™¨æµ‹è¯• (开两个窗口能互通):"
echo "    https://meet.livekit.io/?liveKitUrl=ws%3A%2F%2F${HOST}&token=${CLIENT_TOKEN}"
echo ""
echo "  åœæ­¢æœåŠ¡ï¼š"
echo "    docker compose -f tools/livekit-poc/docker-compose.yml down"
echo "============================================================"
sql/dm/ruoyi-vue-pro-dm8.sql
@@ -14,27 +14,27 @@
-- ----------------------------
CREATE TABLE infra_api_access_log (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    trace_id varchar(64) DEFAULT '' NULL,
    trace_id varchar(64 char) DEFAULT '' NULL,
    user_id bigint DEFAULT 0 NOT NULL,
    user_type smallint DEFAULT 0 NOT NULL,
    application_name varchar(50)  NOT NULL,
    request_method varchar(16) DEFAULT '' NULL,
    request_url varchar(255) DEFAULT '' NULL,
    application_name varchar(50 char)  NOT NULL,
    request_method varchar(16 char) DEFAULT '' NULL,
    request_url varchar(255 char) DEFAULT '' NULL,
    request_params text  NULL,
    response_body text  NULL,
    user_ip varchar(50)  NOT NULL,
    user_agent varchar(512)  NOT NULL,
    operate_module varchar(50) DEFAULT NULL NULL,
    operate_name varchar(50) DEFAULT NULL NULL,
    user_ip varchar(50 char)  NOT NULL,
    user_agent varchar(512 char)  NOT NULL,
    operate_module varchar(50 char) DEFAULT NULL NULL,
    operate_name varchar(50 char) DEFAULT NULL NULL,
    operate_type smallint DEFAULT 0 NULL,
    begin_time datetime  NOT NULL,
    end_time datetime  NOT NULL,
    duration int  NOT NULL,
    result_code int DEFAULT 0 NOT NULL,
    result_msg varchar(512) DEFAULT '' NULL,
    creator varchar(64) DEFAULT '' NULL,
    result_msg varchar(512 char) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -74,30 +74,30 @@
-- ----------------------------
CREATE TABLE infra_api_error_log (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    trace_id varchar(64)  NOT NULL,
    trace_id varchar(64 char)  NOT NULL,
    user_id bigint DEFAULT 0 NOT NULL,
    user_type smallint DEFAULT 0 NOT NULL,
    application_name varchar(50)  NOT NULL,
    request_method varchar(16)  NOT NULL,
    request_url varchar(255)  NOT NULL,
    request_params varchar(8000)  NOT NULL,
    user_ip varchar(50)  NOT NULL,
    user_agent varchar(512)  NOT NULL,
    application_name varchar(50 char)  NOT NULL,
    request_method varchar(16 char)  NOT NULL,
    request_url varchar(255 char)  NOT NULL,
    request_params varchar(8000 char)  NOT NULL,
    user_ip varchar(50 char)  NOT NULL,
    user_agent varchar(512 char)  NOT NULL,
    exception_time datetime  NOT NULL,
    exception_name varchar(128) DEFAULT '' NULL,
    exception_name varchar(128 char) DEFAULT '' NULL,
    exception_message text  NOT NULL,
    exception_root_cause_message text  NOT NULL,
    exception_stack_trace text  NOT NULL,
    exception_class_name varchar(512)  NOT NULL,
    exception_file_name varchar(512)  NOT NULL,
    exception_method_name varchar(512)  NOT NULL,
    exception_class_name varchar(512 char)  NOT NULL,
    exception_file_name varchar(512 char)  NOT NULL,
    exception_method_name varchar(512 char)  NOT NULL,
    exception_line_number int  NOT NULL,
    process_status smallint  NOT NULL,
    process_time datetime DEFAULT NULL NULL,
    process_user_id int DEFAULT 0 NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -141,25 +141,25 @@
CREATE TABLE infra_codegen_column (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    table_id bigint  NOT NULL,
    column_name varchar(200)  NOT NULL,
    data_type varchar(100)  NOT NULL,
    column_comment varchar(500)  NOT NULL,
    column_name varchar(200 char)  NOT NULL,
    data_type varchar(100 char)  NOT NULL,
    column_comment varchar(500 char)  NOT NULL,
    nullable bit  NOT NULL,
    primary_key bit  NOT NULL,
    ordinal_position int  NOT NULL,
    java_type varchar(32)  NOT NULL,
    java_field varchar(64)  NOT NULL,
    dict_type varchar(200) DEFAULT '' NULL,
    example varchar(64) DEFAULT NULL NULL,
    java_type varchar(32 char)  NOT NULL,
    java_field varchar(64 char)  NOT NULL,
    dict_type varchar(200 char) DEFAULT '' NULL,
    example varchar(64 char) DEFAULT NULL NULL,
    create_operation bit  NOT NULL,
    update_operation bit  NOT NULL,
    list_operation bit  NOT NULL,
    list_operation_condition varchar(32) DEFAULT '=' NOT NULL,
    list_operation_condition varchar(32 char) DEFAULT '=' NOT NULL,
    list_operation_result bit  NOT NULL,
    html_type varchar(32)  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    html_type varchar(32 char)  NOT NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -198,14 +198,14 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    data_source_config_id bigint  NOT NULL,
    scene smallint DEFAULT 1 NOT NULL,
    table_name varchar(200) DEFAULT '' NULL,
    table_comment varchar(500) DEFAULT '' NULL,
    remark varchar(500) DEFAULT NULL NULL,
    module_name varchar(30)  NOT NULL,
    business_name varchar(30)  NOT NULL,
    class_name varchar(100) DEFAULT '' NULL,
    class_comment varchar(50)  NOT NULL,
    author varchar(50)  NOT NULL,
    table_name varchar(200 char) DEFAULT '' NULL,
    table_comment varchar(500 char) DEFAULT '' NULL,
    remark varchar(500 char) DEFAULT NULL NULL,
    module_name varchar(30 char)  NOT NULL,
    business_name varchar(30 char)  NOT NULL,
    class_name varchar(100 char) DEFAULT '' NULL,
    class_comment varchar(50 char)  NOT NULL,
    author varchar(50 char)  NOT NULL,
    template_type smallint DEFAULT 1 NOT NULL,
    front_type smallint  NOT NULL,
    parent_menu_id bigint DEFAULT NULL NULL,
@@ -214,9 +214,9 @@
    sub_join_many bit DEFAULT NULL NULL,
    tree_parent_column_id bigint DEFAULT NULL NULL,
    tree_name_column_id bigint DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -252,16 +252,16 @@
-- ----------------------------
CREATE TABLE infra_config (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    category varchar(50)  NOT NULL,
    category varchar(50 char)  NOT NULL,
    type smallint  NOT NULL,
    name varchar(100) DEFAULT '' NULL,
    config_key varchar(100) DEFAULT '' NULL,
    value varchar(500) DEFAULT '' NULL,
    name varchar(100 char) DEFAULT '' NULL,
    config_key varchar(100 char) DEFAULT '' NULL,
    value varchar(500 char) DEFAULT '' NULL,
    visible bit  NOT NULL,
    remark varchar(500) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    remark varchar(500 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -304,13 +304,13 @@
-- ----------------------------
CREATE TABLE infra_data_source_config (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(100) DEFAULT '' NULL,
    url varchar(1024)  NOT NULL,
    username varchar(255)  NOT NULL,
    password varchar(255) DEFAULT '' NULL,
    creator varchar(64) DEFAULT '' NULL,
    name varchar(100 char) DEFAULT '' NULL,
    url varchar(1024 char)  NOT NULL,
    username varchar(255 char)  NOT NULL,
    password varchar(255 char) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -333,14 +333,14 @@
CREATE TABLE infra_file (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    config_id bigint DEFAULT NULL NULL,
    name varchar(256) DEFAULT NULL NULL,
    path varchar(512)  NOT NULL,
    url varchar(1024)  NOT NULL,
    type varchar(128) DEFAULT NULL NULL,
    name varchar(256 char) DEFAULT NULL NULL,
    path varchar(512 char)  NOT NULL,
    url varchar(1024 char)  NOT NULL,
    type varchar(128 char) DEFAULT NULL NULL,
    size int  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -364,14 +364,14 @@
-- ----------------------------
CREATE TABLE infra_file_config (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(63)  NOT NULL,
    name varchar(63 char)  NOT NULL,
    storage smallint  NOT NULL,
    remark varchar(255) DEFAULT NULL NULL,
    remark varchar(255 char) DEFAULT NULL NULL,
    master bit  NOT NULL,
    config varchar(4096)  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    config varchar(4096 char)  NOT NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -415,11 +415,11 @@
CREATE TABLE infra_file_content (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    config_id bigint  NOT NULL,
    path varchar(512)  NOT NULL,
    path varchar(512 char)  NOT NULL,
    content blob  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -442,17 +442,17 @@
-- ----------------------------
CREATE TABLE infra_job (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(32)  NOT NULL,
    name varchar(32 char)  NOT NULL,
    status smallint  NOT NULL,
    handler_name varchar(64)  NOT NULL,
    handler_param varchar(255) DEFAULT NULL NULL,
    cron_expression varchar(32)  NOT NULL,
    handler_name varchar(64 char)  NOT NULL,
    handler_param varchar(255 char) DEFAULT NULL NULL,
    cron_expression varchar(32 char)  NOT NULL,
    retry_count int DEFAULT 0 NOT NULL,
    retry_interval int DEFAULT 0 NOT NULL,
    monitor_timeout int DEFAULT 0 NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -506,17 +506,17 @@
CREATE TABLE infra_job_log (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    job_id bigint  NOT NULL,
    handler_name varchar(64)  NOT NULL,
    handler_param varchar(255) DEFAULT NULL NULL,
    handler_name varchar(64 char)  NOT NULL,
    handler_param varchar(255 char) DEFAULT NULL NULL,
    execute_index smallint DEFAULT 1 NOT NULL,
    begin_time datetime  NOT NULL,
    end_time datetime DEFAULT NULL NULL,
    duration int DEFAULT NULL NULL,
    status smallint  NOT NULL,
    result varchar(4000) DEFAULT '' NULL,
    creator varchar(64) DEFAULT '' NULL,
    result varchar(4000 char) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -546,16 +546,16 @@
-- ----------------------------
CREATE TABLE system_dept (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(30) DEFAULT '' NULL,
    name varchar(30 char) DEFAULT '' NULL,
    parent_id bigint DEFAULT 0 NOT NULL,
    sort int DEFAULT 0 NOT NULL,
    leader_user_id bigint DEFAULT NULL NULL,
    phone varchar(11) DEFAULT NULL NULL,
    email varchar(50) DEFAULT NULL NULL,
    phone varchar(11 char) DEFAULT NULL NULL,
    email varchar(50 char) DEFAULT NULL NULL,
    status smallint  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -608,16 +608,16 @@
CREATE TABLE system_dict_data (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    sort int DEFAULT 0 NOT NULL,
    label varchar(100) DEFAULT '' NULL,
    value varchar(100) DEFAULT '' NULL,
    dict_type varchar(100) DEFAULT '' NULL,
    label varchar(100 char) DEFAULT '' NULL,
    value varchar(100 char) DEFAULT '' NULL,
    dict_type varchar(100 char) DEFAULT '' NULL,
    status smallint DEFAULT 0 NOT NULL,
    color_type varchar(100) DEFAULT '' NULL,
    css_class varchar(100) DEFAULT '' NULL,
    remark varchar(500) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    color_type varchar(100 char) DEFAULT '' NULL,
    css_class varchar(100 char) DEFAULT '' NULL,
    remark varchar(500 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -1270,6 +1270,8 @@
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');
@@ -1552,9 +1554,9 @@
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 (3443, 1, '草稿', '0', 'mes_wm_product_produce_status', 0, 'info', '', '草稿状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3444, 2, '已完成', '4', 'mes_wm_product_produce_status', 0, 'success', '', '已完成状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3445, 3, '已取消', '5', 'mes_wm_product_produce_status', 0, 'danger', '', '已取消状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '0');
COMMIT;
SET IDENTITY_INSERT system_dict_data OFF;
-- @formatter:on
@@ -1564,13 +1566,13 @@
-- ----------------------------
CREATE TABLE system_dict_type (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(100) DEFAULT '' NULL,
    type varchar(100) DEFAULT '' NULL,
    name varchar(100 char) DEFAULT '' NULL,
    type varchar(100 char) DEFAULT '' NULL,
    status smallint DEFAULT 0 NOT NULL,
    remark varchar(500) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    remark varchar(500 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    deleted_time datetime DEFAULT NULL NULL
@@ -1790,16 +1792,16 @@
CREATE TABLE system_login_log (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    log_type bigint  NOT NULL,
    trace_id varchar(64) DEFAULT '' NULL,
    trace_id varchar(64 char) DEFAULT '' NULL,
    user_id bigint DEFAULT 0 NOT NULL,
    user_type smallint DEFAULT 0 NOT NULL,
    username varchar(50) DEFAULT '' NULL,
    username varchar(50 char) DEFAULT '' NULL,
    result smallint  NOT NULL,
    user_ip varchar(50)  NOT NULL,
    user_agent varchar(512)  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    user_ip varchar(50 char)  NOT NULL,
    user_agent varchar(512 char)  NOT NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -1830,16 +1832,16 @@
-- ----------------------------
CREATE TABLE system_mail_account (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    mail varchar(255)  NOT NULL,
    username varchar(255)  NOT NULL,
    password varchar(255)  NOT NULL,
    host varchar(255)  NOT NULL,
    mail varchar(255 char)  NOT NULL,
    username varchar(255 char)  NOT NULL,
    password varchar(255 char)  NOT NULL,
    host varchar(255 char)  NOT NULL,
    port int  NOT NULL,
    ssl_enable bit DEFAULT '0' NOT NULL,
    starttls_enable bit DEFAULT '0' NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -1879,24 +1881,24 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    user_id bigint DEFAULT NULL NULL,
    user_type smallint DEFAULT NULL NULL,
    to_mails varchar(1024)  NOT NULL,
    cc_mails varchar(1024) DEFAULT NULL NULL,
    bcc_mails varchar(1024) DEFAULT NULL NULL,
    to_mails varchar(1024 char)  NOT NULL,
    cc_mails varchar(1024 char) DEFAULT NULL NULL,
    bcc_mails varchar(1024 char) DEFAULT NULL NULL,
    account_id bigint  NOT NULL,
    from_mail varchar(255)  NOT NULL,
    from_mail varchar(255 char)  NOT NULL,
    template_id bigint  NOT NULL,
    template_code varchar(63)  NOT NULL,
    template_nickname varchar(255) DEFAULT NULL NULL,
    template_title varchar(255)  NOT NULL,
    template_code varchar(63 char)  NOT NULL,
    template_nickname varchar(255 char) DEFAULT NULL NULL,
    template_title varchar(255 char)  NOT NULL,
    template_content text  NOT NULL,
    template_params varchar(255)  NOT NULL,
    template_params varchar(255 char)  NOT NULL,
    send_status smallint DEFAULT 0 NOT NULL,
    send_time datetime DEFAULT NULL NULL,
    send_message_id varchar(255) DEFAULT NULL NULL,
    send_exception varchar(4096) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    send_message_id varchar(255 char) DEFAULT NULL NULL,
    send_exception varchar(4096 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -1931,18 +1933,18 @@
-- ----------------------------
CREATE TABLE system_mail_template (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(63)  NOT NULL,
    code varchar(63)  NOT NULL,
    name varchar(63 char)  NOT NULL,
    code varchar(63 char)  NOT NULL,
    account_id bigint  NOT NULL,
    nickname varchar(255) DEFAULT NULL NULL,
    title varchar(255)  NOT NULL,
    content varchar(10240)  NOT NULL,
    params varchar(255)  NOT NULL,
    nickname varchar(255 char) DEFAULT NULL NULL,
    title varchar(255 char)  NOT NULL,
    content varchar(10240 char)  NOT NULL,
    params varchar(255 char)  NOT NULL,
    status smallint  NOT NULL,
    remark varchar(255) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    remark varchar(255 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -1981,22 +1983,22 @@
-- ----------------------------
CREATE TABLE system_menu (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(50)  NOT NULL,
    permission varchar(100) DEFAULT '' NULL,
    name varchar(50 char)  NOT NULL,
    permission varchar(100 char) DEFAULT '' NULL,
    type smallint  NOT NULL,
    sort int DEFAULT 0 NOT NULL,
    parent_id bigint DEFAULT 0 NOT NULL,
    path varchar(200) DEFAULT '' NULL,
    icon varchar(100) DEFAULT '#' NULL,
    component varchar(255) DEFAULT NULL NULL,
    component_name varchar(255) DEFAULT NULL NULL,
    path varchar(200 char) DEFAULT '' NULL,
    icon varchar(100 char) DEFAULT '#' NULL,
    component varchar(255 char) DEFAULT NULL NULL,
    component_name varchar(255 char) DEFAULT NULL NULL,
    status smallint DEFAULT 0 NOT NULL,
    visible bit DEFAULT '1' NOT NULL,
    keep_alive bit DEFAULT '1' NOT NULL,
    always_show bit DEFAULT '1' NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -3357,13 +3359,13 @@
-- ----------------------------
CREATE TABLE system_notice (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    title varchar(50)  NOT NULL,
    title varchar(50 char)  NOT NULL,
    content text  NOT NULL,
    type smallint  NOT NULL,
    status smallint DEFAULT 0 NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3402,16 +3404,16 @@
    user_id bigint  NOT NULL,
    user_type smallint  NOT NULL,
    template_id bigint  NOT NULL,
    template_code varchar(64)  NOT NULL,
    template_nickname varchar(63)  NOT NULL,
    template_content varchar(1024)  NOT NULL,
    template_code varchar(64 char)  NOT NULL,
    template_nickname varchar(63 char)  NOT NULL,
    template_content varchar(1024 char)  NOT NULL,
    template_type int  NOT NULL,
    template_params varchar(255)  NOT NULL,
    template_params varchar(255 char)  NOT NULL,
    read_status bit  NOT NULL,
    read_time datetime DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3461,17 +3463,17 @@
-- ----------------------------
CREATE TABLE system_notify_template (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(63)  NOT NULL,
    code varchar(64)  NOT NULL,
    nickname varchar(255)  NOT NULL,
    content varchar(1024)  NOT NULL,
    name varchar(63 char)  NOT NULL,
    code varchar(64 char)  NOT NULL,
    nickname varchar(255 char)  NOT NULL,
    content varchar(1024 char)  NOT NULL,
    type smallint  NOT NULL,
    params varchar(255) DEFAULT NULL NULL,
    params varchar(255 char) DEFAULT NULL NULL,
    status smallint  NOT NULL,
    remark varchar(255) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    remark varchar(255 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -3499,15 +3501,15 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    user_id bigint  NOT NULL,
    user_type smallint  NOT NULL,
    user_info varchar(512)  NOT NULL,
    access_token varchar(255)  NOT NULL,
    refresh_token varchar(32)  NOT NULL,
    client_id varchar(255)  NOT NULL,
    scopes varchar(255) DEFAULT NULL NULL,
    user_info varchar(512 char)  NOT NULL,
    access_token varchar(255 char)  NOT NULL,
    refresh_token varchar(32 char)  NOT NULL,
    client_id varchar(255 char)  NOT NULL,
    scopes varchar(255 char) DEFAULT NULL NULL,
    expires_time datetime  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3540,13 +3542,13 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    user_id bigint  NOT NULL,
    user_type smallint  NOT NULL,
    client_id varchar(255)  NOT NULL,
    scope varchar(255) DEFAULT '' NULL,
    client_id varchar(255 char)  NOT NULL,
    scope varchar(255 char) DEFAULT '' NULL,
    approved bit DEFAULT '0' NOT NULL,
    expires_time datetime  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3574,24 +3576,24 @@
-- ----------------------------
CREATE TABLE system_oauth2_client (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    client_id varchar(255)  NOT NULL,
    secret varchar(255)  NOT NULL,
    name varchar(255)  NOT NULL,
    logo varchar(255)  NOT NULL,
    description varchar(255) DEFAULT NULL NULL,
    client_id varchar(255 char)  NOT NULL,
    secret varchar(255 char)  NOT NULL,
    name varchar(255 char)  NOT NULL,
    logo varchar(255 char)  NOT NULL,
    description varchar(255 char) DEFAULT NULL NULL,
    status smallint  NOT NULL,
    access_token_validity_seconds int  NOT NULL,
    refresh_token_validity_seconds int  NOT NULL,
    redirect_uris varchar(255)  NOT NULL,
    authorized_grant_types varchar(255)  NOT NULL,
    scopes varchar(255) DEFAULT NULL NULL,
    auto_approve_scopes varchar(255) DEFAULT NULL NULL,
    authorities varchar(255) DEFAULT NULL NULL,
    resource_ids varchar(255) DEFAULT NULL NULL,
    additional_information varchar(4096) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    redirect_uris varchar(255 char)  NOT NULL,
    authorized_grant_types varchar(255 char)  NOT NULL,
    scopes varchar(255 char) DEFAULT NULL NULL,
    auto_approve_scopes varchar(255 char) DEFAULT NULL NULL,
    authorities varchar(255 char) DEFAULT NULL NULL,
    resource_ids varchar(255 char) DEFAULT NULL NULL,
    additional_information varchar(4096 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -3641,15 +3643,15 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    user_id bigint  NOT NULL,
    user_type smallint  NOT NULL,
    code varchar(32)  NOT NULL,
    client_id varchar(255)  NOT NULL,
    scopes varchar(255) DEFAULT '' NULL,
    code varchar(32 char)  NOT NULL,
    client_id varchar(255 char)  NOT NULL,
    scopes varchar(255 char) DEFAULT '' NULL,
    expires_time datetime  NOT NULL,
    redirect_uri varchar(255) DEFAULT NULL NULL,
    state varchar(255) DEFAULT '' NULL,
    creator varchar(64) DEFAULT '' NULL,
    redirect_uri varchar(255 char) DEFAULT NULL NULL,
    state varchar(255 char) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3680,14 +3682,14 @@
CREATE TABLE system_oauth2_refresh_token (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    user_id bigint  NOT NULL,
    refresh_token varchar(32)  NOT NULL,
    refresh_token varchar(32 char)  NOT NULL,
    user_type smallint  NOT NULL,
    client_id varchar(255)  NOT NULL,
    scopes varchar(255) DEFAULT NULL NULL,
    client_id varchar(255 char)  NOT NULL,
    scopes varchar(255 char) DEFAULT NULL NULL,
    expires_time datetime  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3715,22 +3717,22 @@
-- ----------------------------
CREATE TABLE system_operate_log (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    trace_id varchar(64) DEFAULT '' NULL,
    trace_id varchar(64 char) DEFAULT '' NULL,
    user_id bigint  NOT NULL,
    user_type smallint DEFAULT 0 NOT NULL,
    type varchar(50)  NOT NULL,
    sub_type varchar(50)  NOT NULL,
    type varchar(50 char)  NOT NULL,
    sub_type varchar(50 char)  NOT NULL,
    biz_id bigint  NOT NULL,
    action varchar(2000) DEFAULT '' NULL,
    action varchar(2000 char) DEFAULT '' NULL,
    success bit DEFAULT '1' NOT NULL,
    extra varchar(2000) DEFAULT '' NULL,
    request_method varchar(16) DEFAULT '' NULL,
    request_url varchar(255) DEFAULT '' NULL,
    user_ip varchar(50) DEFAULT NULL NULL,
    user_agent varchar(512) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    extra varchar(2000 char) DEFAULT '' NULL,
    request_method varchar(16 char) DEFAULT '' NULL,
    request_url varchar(255 char) DEFAULT '' NULL,
    user_ip varchar(50 char) DEFAULT NULL NULL,
    user_agent varchar(512 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3766,14 +3768,14 @@
-- ----------------------------
CREATE TABLE system_post (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    code varchar(64)  NOT NULL,
    name varchar(50)  NOT NULL,
    code varchar(64 char)  NOT NULL,
    name varchar(50 char)  NOT NULL,
    sort int  NOT NULL,
    status smallint  NOT NULL,
    remark varchar(500) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    remark varchar(500 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3811,17 +3813,17 @@
-- ----------------------------
CREATE TABLE system_role (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(30)  NOT NULL,
    code varchar(100)  NOT NULL,
    name varchar(30 char)  NOT NULL,
    code varchar(100 char)  NOT NULL,
    sort int  NOT NULL,
    data_scope smallint DEFAULT 1 NOT NULL,
    data_scope_dept_ids varchar(500) DEFAULT '' NULL,
    data_scope_dept_ids varchar(500 char) DEFAULT '' NULL,
    status smallint  NOT NULL,
    type smallint  NOT NULL,
    remark varchar(500) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    remark varchar(500 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -3866,9 +3868,9 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    role_id bigint  NOT NULL,
    menu_id bigint  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -4787,16 +4789,16 @@
-- ----------------------------
CREATE TABLE system_sms_channel (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    signature varchar(12)  NOT NULL,
    code varchar(63)  NOT NULL,
    signature varchar(12 char)  NOT NULL,
    code varchar(63 char)  NOT NULL,
    status smallint  NOT NULL,
    remark varchar(255) DEFAULT NULL NULL,
    api_key varchar(128)  NOT NULL,
    api_secret varchar(128) DEFAULT NULL NULL,
    callback_url varchar(255) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    remark varchar(255 char) DEFAULT NULL NULL,
    api_key varchar(128 char)  NOT NULL,
    api_secret varchar(128 char) DEFAULT NULL NULL,
    callback_url varchar(255 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -4833,17 +4835,17 @@
-- ----------------------------
CREATE TABLE system_sms_code (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    mobile varchar(11)  NOT NULL,
    code varchar(6)  NOT NULL,
    create_ip varchar(15)  NOT NULL,
    mobile varchar(11 char)  NOT NULL,
    code varchar(6 char)  NOT NULL,
    create_ip varchar(15 char)  NOT NULL,
    scene smallint  NOT NULL,
    today_index smallint  NOT NULL,
    used smallint  NOT NULL,
    used_time datetime DEFAULT NULL NULL,
    used_ip varchar(255) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    used_ip varchar(255 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -4874,29 +4876,29 @@
CREATE TABLE system_sms_log (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    channel_id bigint  NOT NULL,
    channel_code varchar(63)  NOT NULL,
    channel_code varchar(63 char)  NOT NULL,
    template_id bigint  NOT NULL,
    template_code varchar(63)  NOT NULL,
    template_code varchar(63 char)  NOT NULL,
    template_type smallint  NOT NULL,
    template_content varchar(255)  NOT NULL,
    template_params varchar(255)  NOT NULL,
    api_template_id varchar(63)  NOT NULL,
    mobile varchar(11)  NOT NULL,
    template_content varchar(255 char)  NOT NULL,
    template_params varchar(255 char)  NOT NULL,
    api_template_id varchar(63 char)  NOT NULL,
    mobile varchar(11 char)  NOT NULL,
    user_id bigint DEFAULT NULL NULL,
    user_type smallint DEFAULT NULL NULL,
    send_status smallint DEFAULT 0 NOT NULL,
    send_time datetime DEFAULT NULL NULL,
    api_send_code varchar(63) DEFAULT NULL NULL,
    api_send_msg varchar(255) DEFAULT NULL NULL,
    api_request_id varchar(255) DEFAULT NULL NULL,
    api_serial_no varchar(255) DEFAULT NULL NULL,
    api_send_code varchar(63 char) DEFAULT NULL NULL,
    api_send_msg varchar(255 char) DEFAULT NULL NULL,
    api_request_id varchar(255 char) DEFAULT NULL NULL,
    api_serial_no varchar(255 char) DEFAULT NULL NULL,
    receive_status smallint DEFAULT 0 NOT NULL,
    receive_time datetime DEFAULT NULL NULL,
    api_receive_code varchar(63) DEFAULT NULL NULL,
    api_receive_msg varchar(255) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    api_receive_code varchar(63 char) DEFAULT NULL NULL,
    api_receive_msg varchar(255 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -4937,17 +4939,17 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    type smallint  NOT NULL,
    status smallint  NOT NULL,
    code varchar(63)  NOT NULL,
    name varchar(63)  NOT NULL,
    content varchar(255)  NOT NULL,
    params varchar(255)  NOT NULL,
    remark varchar(255) DEFAULT NULL NULL,
    api_template_id varchar(63)  NOT NULL,
    code varchar(63 char)  NOT NULL,
    name varchar(63 char)  NOT NULL,
    content varchar(255 char)  NOT NULL,
    params varchar(255 char)  NOT NULL,
    remark varchar(255 char) DEFAULT NULL NULL,
    api_template_id varchar(63 char)  NOT NULL,
    channel_id bigint  NOT NULL,
    channel_code varchar(63)  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    channel_code varchar(63 char)  NOT NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -4999,17 +5001,17 @@
-- ----------------------------
CREATE TABLE system_social_client (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(255)  NOT NULL,
    name varchar(255 char)  NOT NULL,
    social_type smallint  NOT NULL,
    user_type smallint  NOT NULL,
    client_id varchar(255)  NOT NULL,
    client_secret varchar(255)  NOT NULL,
    agent_id varchar(255) DEFAULT NULL NULL,
    public_key varchar(2048) DEFAULT NULL NULL,
    client_id varchar(255 char)  NOT NULL,
    client_secret varchar(255 char)  NOT NULL,
    agent_id varchar(255 char) DEFAULT NULL NULL,
    public_key varchar(2048 char) DEFAULT NULL NULL,
    status smallint  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5055,17 +5057,17 @@
CREATE TABLE system_social_user (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    type smallint  NOT NULL,
    openid varchar(32)  NOT NULL,
    token varchar(256) DEFAULT NULL NULL,
    raw_token_info varchar(1024)  NOT NULL,
    nickname varchar(32)  NOT NULL,
    avatar varchar(255) DEFAULT NULL NULL,
    raw_user_info varchar(1024)  NOT NULL,
    code varchar(256)  NOT NULL,
    state varchar(256) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    openid varchar(32 char)  NOT NULL,
    token varchar(256 char) DEFAULT NULL NULL,
    raw_token_info varchar(1024 char)  NOT NULL,
    nickname varchar(32 char)  NOT NULL,
    avatar varchar(255 char) DEFAULT NULL NULL,
    raw_user_info varchar(1024 char)  NOT NULL,
    code varchar(256 char)  NOT NULL,
    state varchar(256 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5101,9 +5103,9 @@
    user_type smallint  NOT NULL,
    social_type smallint  NOT NULL,
    social_user_id bigint  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5129,18 +5131,18 @@
-- ----------------------------
CREATE TABLE system_tenant (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(30)  NOT NULL,
    name varchar(30 char)  NOT NULL,
    contact_user_id bigint DEFAULT NULL NULL,
    contact_name varchar(30)  NOT NULL,
    contact_mobile varchar(500) DEFAULT NULL NULL,
    contact_name varchar(30 char)  NOT NULL,
    contact_mobile varchar(500 char) DEFAULT NULL NULL,
    status smallint DEFAULT 0 NOT NULL,
    websites varchar(1024) DEFAULT '' NULL,
    websites varchar(1024 char) DEFAULT '' NULL,
    package_id bigint  NOT NULL,
    expire_time datetime  NOT NULL,
    account_count int  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -5179,13 +5181,13 @@
-- ----------------------------
CREATE TABLE system_tenant_package (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(30)  NOT NULL,
    name varchar(30 char)  NOT NULL,
    status smallint DEFAULT 0 NOT NULL,
    remark varchar(256) DEFAULT '' NULL,
    menu_ids varchar(4096)  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    remark varchar(256 char) DEFAULT '' NULL,
    menu_ids varchar(4096 char)  NOT NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL
);
@@ -5219,9 +5221,9 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    user_id bigint DEFAULT 0 NOT NULL,
    post_id bigint DEFAULT 0 NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5265,9 +5267,9 @@
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    user_id bigint  NOT NULL,
    role_id bigint  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NULL,
    deleted bit DEFAULT '0' NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5321,22 +5323,22 @@
-- ----------------------------
CREATE TABLE system_users (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    username varchar(30)  NOT NULL,
    password varchar(100) DEFAULT '' NULL,
    nickname varchar(30)  NOT NULL,
    remark varchar(500) DEFAULT NULL NULL,
    username varchar(30 char)  NOT NULL,
    password varchar(100 char) DEFAULT '' NULL,
    nickname varchar(30 char)  NOT NULL,
    remark varchar(500 char) DEFAULT NULL NULL,
    dept_id bigint DEFAULT NULL NULL,
    post_ids varchar(255) DEFAULT NULL NULL,
    email varchar(50) DEFAULT '' NULL,
    mobile varchar(11) DEFAULT '' NULL,
    post_ids varchar(255 char) DEFAULT NULL NULL,
    email varchar(50 char) DEFAULT '' NULL,
    mobile varchar(11 char) DEFAULT '' NULL,
    sex smallint DEFAULT 0 NULL,
    avatar varchar(512) DEFAULT '' NULL,
    avatar varchar(512 char) DEFAULT '' NULL,
    status smallint DEFAULT 0 NOT NULL,
    login_ip varchar(50) DEFAULT '' NULL,
    login_ip varchar(50 char) DEFAULT '' NULL,
    login_date datetime DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5403,14 +5405,14 @@
-- ----------------------------
CREATE TABLE yudao_demo01_contact (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(100) DEFAULT '' NULL,
    name varchar(100 char) DEFAULT '' NULL,
    sex smallint  NOT NULL,
    birthday datetime  NOT NULL,
    description varchar(255)  NOT NULL,
    avatar varchar(512) DEFAULT NULL NULL,
    creator varchar(64) DEFAULT '' NULL,
    description varchar(255 char)  NOT NULL,
    avatar varchar(512 char) DEFAULT NULL NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5445,11 +5447,11 @@
-- ----------------------------
CREATE TABLE yudao_demo02_category (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(100) DEFAULT '' NULL,
    name varchar(100 char) DEFAULT '' NULL,
    parent_id bigint  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5488,11 +5490,11 @@
CREATE TABLE yudao_demo03_course (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    student_id bigint  NOT NULL,
    name varchar(100) DEFAULT '' NULL,
    name varchar(100 char) DEFAULT '' NULL,
    score smallint  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5542,11 +5544,11 @@
CREATE TABLE yudao_demo03_grade (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    student_id bigint  NOT NULL,
    name varchar(100) DEFAULT '' NULL,
    teacher varchar(255)  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    name varchar(100 char) DEFAULT '' NULL,
    teacher varchar(255 char)  NOT NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5581,13 +5583,13 @@
-- ----------------------------
CREATE TABLE yudao_demo03_student (
    id bigint NOT NULL PRIMARY KEY IDENTITY,
    name varchar(100) DEFAULT '' NULL,
    name varchar(100 char) DEFAULT '' NULL,
    sex smallint  NOT NULL,
    birthday datetime  NOT NULL,
    description varchar(255)  NOT NULL,
    creator varchar(64) DEFAULT '' NULL,
    description varchar(255 char)  NOT NULL,
    creator varchar(64 char) DEFAULT '' NULL,
    create_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    updater varchar(64) DEFAULT '' NULL,
    updater varchar(64 char) DEFAULT '' NULL,
    update_time datetime DEFAULT CURRENT_TIMESTAMP NOT NULL,
    deleted bit DEFAULT '0' NOT NULL,
    tenant_id bigint DEFAULT 0 NOT NULL
@@ -5617,4 +5619,3 @@
COMMIT;
SET IDENTITY_INSERT yudao_demo03_student OFF;
-- @formatter:on
sql/highgo/quartz.sql
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,208 @@
-- https://github.com/quartz-scheduler/quartz/blob/main/quartz/src/main/resources/org/quartz/impl/jdbcjobstore/tables_postgres.sql
-- Thanks to Patrick Lightbody for submitting this...
--
-- In your Quartz properties file, you'll need to set
-- org.quartz.jobStore.driverDelegateClass = org.quartz.impl.jdbcjobstore.PostgreSQLDelegate
DROP TABLE IF EXISTS QRTZ_FIRED_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_PAUSED_TRIGGER_GRPS;
DROP TABLE IF EXISTS QRTZ_SCHEDULER_STATE;
DROP TABLE IF EXISTS QRTZ_LOCKS;
DROP TABLE IF EXISTS QRTZ_SIMPLE_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_CRON_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_SIMPROP_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_BLOB_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_TRIGGERS;
DROP TABLE IF EXISTS QRTZ_JOB_DETAILS;
DROP TABLE IF EXISTS QRTZ_CALENDARS;
CREATE TABLE QRTZ_JOB_DETAILS
(
    SCHED_NAME        VARCHAR(120) NOT NULL,
    JOB_NAME          VARCHAR(200) NOT NULL,
    JOB_GROUP         VARCHAR(200) NOT NULL,
    DESCRIPTION       VARCHAR(250) NULL,
    JOB_CLASS_NAME    VARCHAR(250) NOT NULL,
    IS_DURABLE        BOOL         NOT NULL,
    IS_NONCONCURRENT  BOOL         NOT NULL,
    IS_UPDATE_DATA    BOOL         NOT NULL,
    REQUESTS_RECOVERY BOOL         NOT NULL,
    JOB_DATA          BYTEA        NULL,
    PRIMARY KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
);
CREATE TABLE QRTZ_TRIGGERS
(
    SCHED_NAME     VARCHAR(120) NOT NULL,
    TRIGGER_NAME   VARCHAR(200) NOT NULL,
    TRIGGER_GROUP  VARCHAR(200) NOT NULL,
    JOB_NAME       VARCHAR(200) NOT NULL,
    JOB_GROUP      VARCHAR(200) NOT NULL,
    DESCRIPTION    VARCHAR(250) NULL,
    NEXT_FIRE_TIME BIGINT       NULL,
    PREV_FIRE_TIME BIGINT       NULL,
    PRIORITY       INTEGER      NULL,
    TRIGGER_STATE  VARCHAR(16)  NOT NULL,
    TRIGGER_TYPE   VARCHAR(8)   NOT NULL,
    START_TIME     BIGINT       NOT NULL,
    END_TIME       BIGINT       NULL,
    CALENDAR_NAME  VARCHAR(200) NULL,
    MISFIRE_INSTR  SMALLINT     NULL,
    JOB_DATA       BYTEA        NULL,
    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME, JOB_NAME, JOB_GROUP)
        REFERENCES QRTZ_JOB_DETAILS (SCHED_NAME, JOB_NAME, JOB_GROUP)
);
CREATE TABLE QRTZ_SIMPLE_TRIGGERS
(
    SCHED_NAME      VARCHAR(120) NOT NULL,
    TRIGGER_NAME    VARCHAR(200) NOT NULL,
    TRIGGER_GROUP   VARCHAR(200) NOT NULL,
    REPEAT_COUNT    BIGINT       NOT NULL,
    REPEAT_INTERVAL BIGINT       NOT NULL,
    TIMES_TRIGGERED BIGINT       NOT NULL,
    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_CRON_TRIGGERS
(
    SCHED_NAME      VARCHAR(120) NOT NULL,
    TRIGGER_NAME    VARCHAR(200) NOT NULL,
    TRIGGER_GROUP   VARCHAR(200) NOT NULL,
    CRON_EXPRESSION VARCHAR(120) NOT NULL,
    TIME_ZONE_ID    VARCHAR(80),
    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_SIMPROP_TRIGGERS
(
    SCHED_NAME    VARCHAR(120)   NOT NULL,
    TRIGGER_NAME  VARCHAR(200)   NOT NULL,
    TRIGGER_GROUP VARCHAR(200)   NOT NULL,
    STR_PROP_1    VARCHAR(512)   NULL,
    STR_PROP_2    VARCHAR(512)   NULL,
    STR_PROP_3    VARCHAR(512)   NULL,
    INT_PROP_1    INT            NULL,
    INT_PROP_2    INT            NULL,
    LONG_PROP_1   BIGINT         NULL,
    LONG_PROP_2   BIGINT         NULL,
    DEC_PROP_1    NUMERIC(13, 4) NULL,
    DEC_PROP_2    NUMERIC(13, 4) NULL,
    BOOL_PROP_1   BOOL           NULL,
    BOOL_PROP_2   BOOL           NULL,
    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_BLOB_TRIGGERS
(
    SCHED_NAME    VARCHAR(120) NOT NULL,
    TRIGGER_NAME  VARCHAR(200) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    BLOB_DATA     BYTEA        NULL,
    PRIMARY KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP),
    FOREIGN KEY (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
        REFERENCES QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_CALENDARS
(
    SCHED_NAME    VARCHAR(120) NOT NULL,
    CALENDAR_NAME VARCHAR(200) NOT NULL,
    CALENDAR      BYTEA        NOT NULL,
    PRIMARY KEY (SCHED_NAME, CALENDAR_NAME)
);
CREATE TABLE QRTZ_PAUSED_TRIGGER_GRPS
(
    SCHED_NAME    VARCHAR(120) NOT NULL,
    TRIGGER_GROUP VARCHAR(200) NOT NULL,
    PRIMARY KEY (SCHED_NAME, TRIGGER_GROUP)
);
CREATE TABLE QRTZ_FIRED_TRIGGERS
(
    SCHED_NAME        VARCHAR(120) NOT NULL,
    ENTRY_ID          VARCHAR(95)  NOT NULL,
    TRIGGER_NAME      VARCHAR(200) NOT NULL,
    TRIGGER_GROUP     VARCHAR(200) NOT NULL,
    INSTANCE_NAME     VARCHAR(200) NOT NULL,
    FIRED_TIME        BIGINT       NOT NULL,
    SCHED_TIME        BIGINT       NOT NULL,
    PRIORITY          INTEGER      NOT NULL,
    STATE             VARCHAR(16)  NOT NULL,
    JOB_NAME          VARCHAR(200) NULL,
    JOB_GROUP         VARCHAR(200) NULL,
    IS_NONCONCURRENT  BOOL         NULL,
    REQUESTS_RECOVERY BOOL         NULL,
    PRIMARY KEY (SCHED_NAME, ENTRY_ID)
);
CREATE TABLE QRTZ_SCHEDULER_STATE
(
    SCHED_NAME        VARCHAR(120) NOT NULL,
    INSTANCE_NAME     VARCHAR(200) NOT NULL,
    LAST_CHECKIN_TIME BIGINT       NOT NULL,
    CHECKIN_INTERVAL  BIGINT       NOT NULL,
    PRIMARY KEY (SCHED_NAME, INSTANCE_NAME)
);
CREATE TABLE QRTZ_LOCKS
(
    SCHED_NAME VARCHAR(120) NOT NULL,
    LOCK_NAME  VARCHAR(40)  NOT NULL,
    PRIMARY KEY (SCHED_NAME, LOCK_NAME)
);
CREATE INDEX IDX_QRTZ_J_REQ_RECOVERY
    ON QRTZ_JOB_DETAILS (SCHED_NAME, REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_J_GRP
    ON QRTZ_JOB_DETAILS (SCHED_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_J
    ON QRTZ_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_JG
    ON QRTZ_TRIGGERS (SCHED_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_T_C
    ON QRTZ_TRIGGERS (SCHED_NAME, CALENDAR_NAME);
CREATE INDEX IDX_QRTZ_T_G
    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_T_STATE
    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_STATE
    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_N_G_STATE
    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_GROUP, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NEXT_FIRE_TIME
    ON QRTZ_TRIGGERS (SCHED_NAME, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST
    ON QRTZ_TRIGGERS (SCHED_NAME, TRIGGER_STATE, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_MISFIRE
    ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE
    ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_T_NFT_ST_MISFIRE_GRP
    ON QRTZ_TRIGGERS (SCHED_NAME, MISFIRE_INSTR, NEXT_FIRE_TIME, TRIGGER_GROUP, TRIGGER_STATE);
CREATE INDEX IDX_QRTZ_FT_TRIG_INST_NAME
    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME);
CREATE INDEX IDX_QRTZ_FT_INST_JOB_REQ_RCVRY
    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, INSTANCE_NAME, REQUESTS_RECOVERY);
CREATE INDEX IDX_QRTZ_FT_J_G
    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_JG
    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, JOB_GROUP);
CREATE INDEX IDX_QRTZ_FT_T_G
    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_NAME, TRIGGER_GROUP);
CREATE INDEX IDX_QRTZ_FT_TG
    ON QRTZ_FIRED_TRIGGERS (SCHED_NAME, TRIGGER_GROUP);
COMMIT;
sql/highgo/ruoyi-vue-pro.sql
¶Ô±ÈÐÂÎļþ
ÎļþÌ«´ó
sql/kingbase/ruoyi-vue-pro.sql
@@ -1371,6 +1371,8 @@
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');
@@ -1653,9 +1655,9 @@
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 (3443, 1, '草稿', '0', 'mes_wm_product_produce_status', 0, 'info', '', '草稿状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3444, 2, '已完成', '4', 'mes_wm_product_produce_status', 0, 'success', '', '已完成状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3445, 3, '已取消', '5', 'mes_wm_product_produce_status', 0, 'danger', '', '已取消状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '0');
COMMIT;
-- @formatter:on
@@ -5943,4 +5945,3 @@
DROP SEQUENCE IF EXISTS yudao_demo03_student_seq;
CREATE SEQUENCE yudao_demo03_student_seq
    START 10;
sql/mdm_migration.sql
ÎļþÒÑɾ³ý
sql/mdm_tables.sql
ÎļþÒÑɾ³ý
sql/mysql/ai.sql
ÎļþÒÑɾ³ý
sql/mysql/ruoyi-vue-pro.sql
@@ -11,7 +11,7 @@
 Target Server Version : 80200 (8.2.0)
 File Encoding         : 65001
 Date: 16/05/2026 06:24:32
 Date: 29/06/2026 17:18:58
*/
SET NAMES utf8mb4;
@@ -49,7 +49,7 @@
  `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 = 36301 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API è®¿é—®æ—¥å¿—表';
) ENGINE = InnoDB AUTO_INCREMENT = 36346 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'API è®¿é—®æ—¥å¿—表';
-- ----------------------------
-- Records of infra_api_access_log
@@ -92,7 +92,7 @@
  `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 = 23883 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
) ENGINE = InnoDB AUTO_INCREMENT = 24453 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统异常日志';
-- ----------------------------
-- Records of infra_api_error_log
@@ -253,7 +253,7 @@
  `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 = 2216 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
) ENGINE = InnoDB AUTO_INCREMENT = 2260 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
-- ----------------------------
-- Records of infra_file
@@ -284,17 +284,17 @@
-- Records of infra_file_config
-- ----------------------------
BEGIN;
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库(示例)', 1, '我是数据库', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, '七牛存储器(示例)', 20, '请换成你自己的密钥!!!', b'1', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-01-13 22:11:12', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (24, '腾讯云存储(示例)', 20, '请换成你的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"https://cos.ap-shanghai.myqcloud.com\",\"domain\":\"http://tengxun-oss.iocoder.cn\",\"bucket\":\"aoteman-1255880240\",\"accessKey\":\"AKIDAF6WSh1uiIjwqtrOsGSN3WryqTM6cTMt\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:03:22', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (25, '阿里云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"oss-cn-beijing.aliyuncs.com\",\"domain\":\"http://ali-oss.iocoder.cn\",\"bucket\":\"yunai-aoteman\",\"accessKey\":\"LTAI5tEQLgnDyjh3WpNcdMKA\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:47:08', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (26, '火山云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"tos-s3-cn-beijing.volces.com\",\"domain\":null,\"bucket\":\"yunai\",\"accessKey\":\"AKLTZjc3Zjc4MzZmMjU3NDk0ZTgxYmIyMmFkNTIwMDI1ZGE\",\"accessSecret\":\"X==\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:56:42', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (27, '华为云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"obs.cn-east-3.myhuaweicloud.com\",\"domain\":\"\",\"bucket\":\"yudao\",\"accessKey\":\"PVDONDEIOTW88LF8DC4U\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 17:18:41', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (28, 'MinIO å­˜å‚¨ï¼ˆç¤ºä¾‹ï¼‰', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"http://127.0.0.1:9000\",\"domain\":\"http://127.0.0.1:9000/yudao\",\"bucket\":\"yudao\",\"accessKey\":\"admin\",\"accessSecret\":\"password\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 17:43:10', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (29, '本地存储(示例)', 10, 'mac/linux ä½¿ç”¨ /,windows ä½¿ç”¨ \\', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig\",\"basePath\":\"/Users/yunai/tmp/file\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2025-05-02 11:25:45', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (30, 'SFTP å­˜å‚¨ï¼ˆç¤ºä¾‹ï¼‰', 12, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig\",\"basePath\":\"/upload\",\"domain\":\"http://127.0.0.1:48080\",\"host\":\"127.0.0.1\",\"port\":2222,\"username\":\"foo\",\"password\":\"pass\"}', '1', '2025-05-02 16:34:10', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (34, '七牛云存储【私有】(示例)', 20, '请换成你自己的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://t151glocd.hn-bkt.clouddn.com\",\"bucket\":\"ruoyi-vue-pro-private\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":false}', '1', '2025-08-17 21:22:00', '1', '2025-11-24 20:57:14', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (35, '1', 20, '1', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"http://www.baidu.com\",\"domain\":\"http://www.xxx.com\",\"bucket\":\"1\",\"accessKey\":\"2\",\"accessSecret\":\"3\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":false,\"region\":\"1\"}', '1', '2025-10-02 14:32:12', '1', '2025-11-29 15:59:39', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (4, '数据库(示例)', 1, '我是数据库', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.db.DBFileClientConfig\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2022-03-15 23:56:24', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (22, '七牛存储器(示例)', 20, '请换成你自己的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://test.yudao.iocoder.cn\",\"bucket\":\"ruoyi-vue-pro\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-01-13 22:11:12', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (24, '腾讯云存储(示例)', 20, '请换成你的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"https://cos.ap-shanghai.myqcloud.com\",\"domain\":\"http://tengxun-oss.iocoder.cn\",\"bucket\":\"aoteman-1255880240\",\"accessKey\":\"AKIDAF6WSh1uiIjwqtrOsGSN3WryqTM6cTMt\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:03:22', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (25, '阿里云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"oss-cn-beijing.aliyuncs.com\",\"domain\":\"http://ali-oss.iocoder.cn\",\"bucket\":\"yunai-aoteman\",\"accessKey\":\"LTAI5tEQLgnDyjh3WpNcdMKA\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:47:08', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (26, '火山云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"tos-s3-cn-beijing.volces.com\",\"domain\":null,\"bucket\":\"yunai\",\"accessKey\":\"AKLTZjc3Zjc4MzZmMjU3NDk0ZTgxYmIyMmFkNTIwMDI1ZGE\",\"accessSecret\":\"X==\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 16:56:42', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (27, '华为云存储(示例)', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"obs.cn-east-3.myhuaweicloud.com\",\"domain\":\"\",\"bucket\":\"yudao\",\"accessKey\":\"PVDONDEIOTW88LF8DC4U\",\"accessSecret\":\"X\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 17:18:41', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (28, 'MinIO å­˜å‚¨ï¼ˆç¤ºä¾‹ï¼‰', 20, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"http://127.0.0.1:9000\",\"domain\":\"http://127.0.0.1:9000/yudao\",\"bucket\":\"yudao\",\"accessKey\":\"admin\",\"accessSecret\":\"password\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":true}', '1', '2024-11-09 17:43:10', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (29, '本地存储(示例)', 10, 'mac/linux ä½¿ç”¨ /,windows ä½¿ç”¨ \\', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.local.LocalFileClientConfig\",\"basePath\":\"/Users/yunai/tmp/file\",\"domain\":\"http://127.0.0.1:48080\"}', '1', '2025-05-02 11:25:45', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (30, 'SFTP å­˜å‚¨ï¼ˆç¤ºä¾‹ï¼‰', 12, '', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.sftp.SftpFileClientConfig\",\"basePath\":\"/upload\",\"domain\":\"http://127.0.0.1:48080\",\"host\":\"127.0.0.1\",\"port\":2222,\"username\":\"foo\",\"password\":\"pass\"}', '1', '2025-05-02 16:34:10', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (34, '七牛云存储【私有】(示例)', 20, '请换成你自己的密钥!!!', b'0', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"s3.cn-south-1.qiniucs.com\",\"domain\":\"http://t151glocd.hn-bkt.clouddn.com\",\"bucket\":\"ruoyi-vue-pro-private\",\"accessKey\":\"3TvrJ70gl2Gt6IBe7_IZT1F6i_k0iMuRtyEv4EyS\",\"accessSecret\":\"wd0tbVBYlp0S-ihA8Qg2hPLncoP83wyrIq24OZuY\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":false}', '1', '2025-08-17 21:22:00', '1', '2026-06-26 12:23:25', b'0');
INSERT INTO `infra_file_config` (`id`, `name`, `storage`, `remark`, `master`, `config`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (35, '1', 20, '1', b'1', '{\"@class\":\"cn.iocoder.yudao.module.infra.framework.file.core.client.s3.S3FileClientConfig\",\"endpoint\":\"http://www.baidu.com\",\"domain\":\"http://www.xxx.com\",\"bucket\":\"1\",\"accessKey\":\"2\",\"accessSecret\":\"3\",\"enablePathStyleAccess\":false,\"enablePublicAccess\":false,\"region\":\"1\"}', '1', '2025-10-02 14:32:12', '1', '2026-06-26 12:23:25', b'0');
COMMIT;
-- ----------------------------
@@ -313,7 +313,7 @@
  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_config_id_path`(`config_id` ASC, `path` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 286 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
) ENGINE = InnoDB AUTO_INCREMENT = 288 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '文件表';
-- ----------------------------
-- Records of infra_file_content
@@ -462,7 +462,7 @@
  `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 = 1061096 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典数据表';
) ENGINE = InnoDB AUTO_INCREMENT = 1061137 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`, `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', '2026-05-23 13:52:25', '1', '2026-05-23 13:52:25', 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', '2026-05-23 13:52:25', '1', '2026-05-23 13:52:25', 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');
@@ -1372,17 +1374,14 @@
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 (3443, 1, '草稿', '0', 'mes_wm_product_produce_status', 0, 'info', '', '草稿状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3444, 2, '已完成', '4', 'mes_wm_product_produce_status', 0, 'success', '', '已完成状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3445, 3, '已取消', '5', 'mes_wm_product_produce_status', 0, 'danger', '', '已取消状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', b'0');
INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', 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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', 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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', 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 (3520, 1, '未读', '0', 'im_private_message_status', 0, 'warning', '', '私聊=未读,群聊=正常', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 15:14: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 (3521, 2, '已撤回', '2', 'im_private_message_status', 0, 'danger', '', 'RECALL', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 15:14: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 (3522, 3, '已读', '3', 'im_private_message_status', 0, 'success', '', 'READ(仅私聊)', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 15:14: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 (3525, 1, '正常', '0', 'im_group_message_status', 0, 'success', '', '群聊正常(初始状态)', 'admin', '2026-04-30 15:14:36', 'admin', '2026-04-30 15:14: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 (3526, 2, '已撤回', '2', 'im_group_message_status', 0, 'danger', '', '群聊已撤回', 'admin', '2026-04-30 15:14:36', 'admin', '2026-04-30 15:14: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 (3530, 1, '不需要回执', '0', 'im_group_message_receipt_status', 0, 'info', '', 'NO_RECEIPT', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', 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 (3531, 2, '待完成', '1', 'im_group_message_receipt_status', 0, 'warning', '', 'PENDING', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', 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 (3532, 3, '已完成', '2', 'im_group_message_receipt_status', 0, 'success', '', 'DONE', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', 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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, 'default', '', 'PREPARE', '1', '2026-04-16 09:47:00', '1', '2026-06-13 10:32:58', 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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, 'success', '', 'FINISHED', '1', '2026-04-16 09:47:00', '1', '2026-06-13 10:32:58', 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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, 'danger', '', 'CANCELED', '1', '2026-04-16 09:47:00', '1', '2026-06-13 10:32:58', 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 (3525, 1, '正常', '0', 'im_message_status', 0, 'success', '', '正常(初始状态)', 'admin', '2026-04-30 15:14:36', 'admin', '2026-06-16 03:41: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 (3526, 2, '已撤回', '2', 'im_message_status', 0, 'danger', '', '已撤回', 'admin', '2026-04-30 15:14:36', 'admin', '2026-06-16 03:41: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 (3530, 1, '不需要回执', '0', 'im_message_receipt_status', 0, 'info', '', 'NO_RECEIPT', 'admin', '2026-04-30 11:35:07', 'admin', '2026-06-14 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 (3531, 2, '待完成', '1', 'im_message_receipt_status', 0, 'warning', '', 'PENDING', 'admin', '2026-04-30 11:35:07', 'admin', '2026-06-14 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 (3532, 3, '已完成', '2', 'im_message_receipt_status', 0, 'success', '', 'DONE', 'admin', '2026-04-30 11:35:07', 'admin', '2026-06-14 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 (3540, 1, '正常', '0', 'im_friend_status', 0, 'success', '', '正常好友关系', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', 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 (3541, 2, '已删除', '1', 'im_friend_status', 0, 'danger', '', '已删除好友关系', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', 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 (3550, 1, '正常', '0', 'im_group_status', 0, 'success', '', '群正常', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', b'0');
@@ -1397,39 +1396,39 @@
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 (3559, 1, '等待验证', '0', 'im_friend_request_handle_result', 0, 'warning', '', NULL, '1', '2026-05-04 02:43:41', '1', '2026-05-04 02:43:41', 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 (3560, 2, '已添加', '1', 'im_friend_request_handle_result', 0, 'success', '', NULL, '1', '2026-05-04 02:43:41', '1', '2026-05-04 02:43:41', 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 (3561, 3, '已拒绝', '2', 'im_friend_request_handle_result', 0, 'info', '', NULL, '1', '2026-05-04 02:43:41', '1', '2026-05-04 02:43:41', 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 (3562, 101, '文本', '101', 'im_message_type', 0, '', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3563, 102, '图片', '102', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3564, 103, '语音', '103', 'im_message_type', 0, 'warning', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3565, 104, '视频', '104', 'im_message_type', 0, 'warning', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3566, 105, '文件', '105', 'im_message_type', 0, 'info', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3568, 2101, '撤回', '2101', 'im_message_type', 0, 'danger', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3569, 2200, '回执', '2200', 'im_message_type', 0, 'warning', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3570, 2201, '已读', '2201', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3571, 1501, '群创建', '1501', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3572, 1502, '群信息变更', '1502', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3573, 1503, '入群申请', '1503', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3574, 1504, '成员退群', '1504', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3575, 1505, '入群申请通过', '1505', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3576, 1506, '入群申请拒绝', '1506', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3577, 1507, '群主转让', '1507', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3578, 1508, '成员被移出', '1508', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3579, 1509, '成员加入', '1509', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3580, 1510, '自由进群', '1510', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3581, 1511, '群解散', '1511', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3582, 1512, '成员禁言', '1512', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3583, 1513, '成员取消禁言', '1513', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3584, 1514, '全群禁言', '1514', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3585, 1515, '全群取消禁言', '1515', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3586, 1516, '成员昵称变更', '1516', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3587, 1517, '添加管理员', '1517', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3588, 1518, '撤销管理员', '1518', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3589, 1519, '群公告变更', '1519', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3590, 1520, '群名变更', '1520', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3591, 1531, '群消息置顶', '1531', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3592, 1532, '群消息取消置顶', '1532', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3593, 1533, '群封禁变更', '1533', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-05-05 11:52:30', 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 (3594, 1204, '新增好友', '1204', 'im_message_type', 0, 'success', '', NULL, 'admin', '2026-05-05 13:26:53', 'admin', '2026-05-05 13:26:53', 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 (3595, 1205, '好友被删除', '1205', 'im_message_type', 0, 'warning', '', NULL, 'admin', '2026-05-05 13:26:53', 'admin', '2026-05-05 13:26:53', 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 (3562, 101, '文本', '101', 'im_content_type', 0, '', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3563, 102, '图片', '102', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3564, 103, '语音', '103', 'im_content_type', 0, 'warning', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3565, 104, '视频', '104', 'im_content_type', 0, 'warning', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3566, 105, '文件', '105', 'im_content_type', 0, 'info', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3568, 2101, '撤回', '2101', 'im_content_type', 0, 'danger', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3569, 2200, '回执', '2200', 'im_content_type', 0, 'warning', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3570, 2201, '已读', '2201', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3571, 1501, '群创建', '1501', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3572, 1502, '群信息变更', '1502', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3573, 1503, '收到新的入群申请', '1503', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:47:00', 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 (3574, 1504, '成员退群', '1504', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3575, 1505, '入群申请被同意', '1505', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:47:00', 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 (3576, 1506, '入群申请被拒绝', '1506', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:47:00', 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 (3577, 1507, '群主转让', '1507', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3578, 1508, '成员被移出', '1508', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3579, 1509, '成员加入', '1509', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3580, 1510, '自由进群', '1510', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3581, 1511, '群解散', '1511', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3582, 1512, '成员禁言', '1512', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3583, 1513, '成员取消禁言', '1513', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3584, 1514, '全群禁言', '1514', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3585, 1515, '全群取消禁言', '1515', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3586, 1516, '成员昵称变更', '1516', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3587, 1517, '添加管理员', '1517', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3588, 1518, '撤销管理员', '1518', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3589, 1519, '群公告变更', '1519', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3590, 1520, '群名变更', '1520', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3591, 1531, '群消息置顶', '1531', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3592, 1532, '群消息取消置顶', '1532', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3593, 1533, '群封禁变更', '1533', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 11:52:30', 'admin', '2026-06-16 03:41: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 (3594, 1204, '新增好友', '1204', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-05-05 13:26:53', 'admin', '2026-06-16 03:41: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 (3595, 1205, '好友被删除', '1205', 'im_content_type', 0, 'warning', '', NULL, 'admin', '2026-05-05 13:26:53', 'admin', '2026-06-16 03:41: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 (3596, 1, '搜索', '1', 'im_group_add_source', 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 (3597, 2, '邀请', '2', 'im_group_add_source', 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 (3598, 3, '扫码', '3', 'im_group_add_source', 0, '', '', NULL, '', '2026-05-06 09:26:36', '', '2026-05-06 09:26:36', b'0');
@@ -1454,8 +1453,47 @@
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');
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 (1061096, 1, '语音', '1', 'im_rtc_call_media_type', 0, '', '', '语音通话', 'admin', '2026-05-16 11:34:50', 'admin', '2026-05-16 11:34:50', 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 (1061097, 2, '视频', '2', 'im_rtc_call_media_type', 0, '', '', '视频通话', 'admin', '2026-05-16 11:34:50', 'admin', '2026-05-16 11:34:50', 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 (1061098, 1, '私聊', '1', 'im_rtc_call_conversation_type', 0, 'primary', '', '一对一私聊通话', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061099, 2, '群聊', '2', 'im_rtc_call_conversation_type', 0, 'success', '', '群内多人通话', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061100, 1, '创建', '10', 'im_rtc_call_status', 0, 'info', '', '通话已创建,等待接通', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061101, 2, '进行中', '20', 'im_rtc_call_status', 0, 'primary', '', '已有人接通,通话中', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061102, 3, '已结束', '30', 'im_rtc_call_status', 0, 'success', '', '通话结束', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061103, 1, '通话结束', '1', 'im_rtc_call_end_reason', 0, 'success', '', '接通后任一方主动挂断', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061104, 2, '已拒绝', '2', 'im_rtc_call_end_reason', 0, 'warning', '', '被叫接通前点拒接', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061105, 3, '已取消', '3', 'im_rtc_call_end_reason', 0, 'info', '', '主叫接通前主动取消', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061106, 4, '无人接听', '4', 'im_rtc_call_end_reason', 0, 'info', '', '振铃超时未接通', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061107, 5, '对方正忙', '5', 'im_rtc_call_end_reason', 0, 'warning', '', '对方在另一通话中', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061108, 6, '通话异常', '9', 'im_rtc_call_end_reason', 0, 'danger', '', '网络中断、设备失败等', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061109, 1, '发起人', '1', 'im_rtc_participant_role', 0, 'primary', '', '通话发起者', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061110, 2, '被邀请者', '2', 'im_rtc_participant_role', 0, 'info', '', '被邀请加入', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061111, 3, '主动加入者', '3', 'im_rtc_participant_role', 0, 'success', '', '群通话场景,旁观者主动加入', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061112, 1, '邀请中', '10', 'im_rtc_participant_status', 0, 'info', '', '已发出 invite,等待响应', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061113, 2, '已加入', '20', 'im_rtc_participant_status', 0, 'primary', '', '已接通并进入房间', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061114, 3, '已拒绝', '30', 'im_rtc_participant_status', 0, 'warning', '', '接通前点拒接', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061115, 4, '未应答', '40', 'im_rtc_participant_status', 0, 'info', '', '通话已结束仍未应答', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061116, 5, '已离开', '50', 'im_rtc_participant_status', 0, 'success', '', '接通后挂断 / ç¦»å¼€', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', 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 (1061117, 1610, '通话开始', '1610', 'im_content_type', 0, 'info', '', '入消息流;私聊定向通知,群聊全员广播', 'admin', '2026-05-18 03:36:12', 'admin', '2026-06-16 03:41: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 (1061118, 1611, '通话结束', '1611', 'im_content_type', 0, 'info', '', '入消息流;私聊准气泡,群聊系统 tip', 'admin', '2026-05-18 03:36:12', 'admin', '2026-06-16 03:41: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 (1061119, 125, '素材', '125', 'im_content_type', 0, 'success', '', '频道运营推送的图文卡片消息', '1', '2026-05-18 13:14:34', 'admin', '2026-06-16 03:41: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 (1061120, 1, '富文本', '1', 'im_channel_material_type', 0, 'primary', '', '', '1', '2026-05-19 14:09:25', '1', '2026-05-19 14:09:25', 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 (1061121, 2, '外链', '2', 'im_channel_material_type', 0, 'info', '', '', '1', '2026-05-19 14:09:25', '1', '2026-05-19 14:09:25', 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 (1061122, 7, 'SN ç ', '109', 'mes_wm_barcode_biz_type', 0, 'default', '', 'SN', '1', '2026-06-13 10:30:30', '1', '2026-06-13 10:30:30', 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 (1061123, 107, '合并转发', '107', 'im_content_type', 0, '', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061124, 108, '名片', '108', 'im_content_type', 0, '', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061125, 115, '表情', '115', 'im_content_type', 0, '', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061126, 1201, '好友申请被同意', '1201', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061127, 1202, '好友申请被拒绝', '1202', 'im_content_type', 0, 'warning', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061128, 1203, '收到新的好友申请', '1203', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061129, 1207, '加入黑名单', '1207', 'im_content_type', 0, 'danger', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061130, 1208, '移出黑名单', '1208', 'im_content_type', 0, 'success', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061131, 1209, '好友资料变更', '1209', 'im_content_type', 0, 'info', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061132, 1210, '好友信息批量更新', '1210', 'im_content_type', 0, 'info', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061133, 1530, '群成员个人设置变更', '1530', 'im_content_type', 0, 'info', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061134, 1601, '通话信令', '1601', 'im_content_type', 0, 'info', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061135, 1602, '通话参与者加入', '1602', 'im_content_type', 0, 'info', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', 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 (1061136, 1603, '通话参与者离开', '1603', 'im_content_type', 0, 'info', '', NULL, 'admin', '2026-06-16 03:47:00', 'admin', '2026-06-16 03:47:00', b'0');
COMMIT;
-- ----------------------------
@@ -1475,7 +1513,7 @@
  `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 = 1061092 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
) ENGINE = InnoDB AUTO_INCREMENT = 1061099 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '字典类型表';
-- ----------------------------
-- Records of system_dict_type
@@ -1613,7 +1651,7 @@
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2029, 'MES å·¥å•类型', 'mes_pro_work_order_type', 0, 'MES ç”Ÿäº§ç®¡ç† - å·¥å•类型(1=自行生产,2=代工,3=采购)', '1', '2026-02-17 11:43:47', '1', '2026-02-17 11:43:47', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2036, 'MES å·¥åºå…³ç³»ç±»åž‹', 'mes_pro_link_type', 0, '工艺路线中工序之间的关系类型', '1', '2026-02-19 04:24:53', '1', '2026-04-05 15:05:07', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2037, 'MES æ—¶é—´å•位', 'mes_time_unit_type', 0, '生产时间的计量单位', '1', '2026-02-19 04:24:53', '1', '2026-04-05 15:04:57', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2038, 'MES ç”Ÿäº§ä»»åŠ¡çŠ¶æ€', 'mes_pro_task_status', 0, 'MES ç”Ÿäº§ç®¡ç† - ä»»åŠ¡çŠ¶æ€ï¼ˆ0=草稿,1=进行中,2=暂停,3=已完成,4=已取消)', '1', '2026-02-19 15:25:27', '1', '2026-02-19 15:25:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2038, 'MES ç”Ÿäº§ä»»åŠ¡çŠ¶æ€', 'mes_pro_task_status', 0, 'MES ç”Ÿäº§ç®¡ç† - ä»»åŠ¡çŠ¶æ€ï¼ˆ0=草稿,4=已完成,5=已取消)', '1', '2026-02-19 15:25:27', '1', '2026-06-13 10:32:58', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2039, 'MES ç‚¹æ£€ä¿å…»é¡¹ç›®ç±»åž‹', 'mes_dv_subject_type', 0, 'MES è®¾å¤‡ç®¡ç† - ç‚¹æ£€ä¿å…»é¡¹ç›®ç±»åž‹ï¼ˆ1=设备点检,2=设备保养)', '1', '2026-02-20 01:42:58', '1', '2026-02-20 01:42:58', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2040, 'MES ä¿å…»è®°å½•状态', 'mes_mainten_record_status', 0, NULL, 'admin', '2026-02-20 02:59:55', 'admin', '2026-02-20 02:59:55', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2041, 'MES ä¿å…»ç»“æžœ', 'mes_mainten_status', 0, NULL, 'admin', '2026-02-20 02:59:55', 'admin', '2026-02-20 02:59:55', b'0', NULL);
@@ -1666,12 +1704,11 @@
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2133, 'MES é”€å”®é€€è´§å•状态', 'mes_wm_return_sales_status', 0, 'MES é”€å”®é€€è´§å•状态枚举', '1', '2026-04-03 17:20:25', '1', '2026-04-05 15:05:39', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2135, 'MES ä¸Šä¸‹å·¥çŠ¶æ€ç±»åž‹', 'mes_pro_work_record_type', 0, 'MES ä¸Šä¸‹å·¥çŠ¶æ€ç±»åž‹', '1', '2026-04-05 14:07:27', '1', '2026-04-05 14:07:27', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2138, 'MES ç”Ÿäº§å…¥åº“单状态', 'mes_wm_product_produce_status', 0, 'MES ç”Ÿäº§å…¥åº“单状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', b'0', '1970-01-01 00:00:00');
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2200, 'IM æ¶ˆæ¯ç±»åž‹', 'im_message_type', 0, '对应 ImMessageTypeEnum', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2201, 'IM ç§èŠæ¶ˆæ¯çŠ¶æ€', 'im_private_message_status', 0, '对应 ImMessageStatusEnum;私聊 0=未读 / 2=已撤回 / 3=已读', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 15:14:36', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2202, 'IM ç¾¤æ¶ˆæ¯å›žæ‰§çŠ¶æ€', 'im_group_message_receipt_status', 0, '对应 ImGroupMessageReceiptStatusEnum', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2200, 'IM å†…容类型', 'im_content_type', 0, '对应 ImContentTypeEnum', 'admin', '2026-04-30 11:35:07', 'admin', '2026-06-16 03:41:55', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2202, 'IM æ¶ˆæ¯å›žæ‰§çŠ¶æ€', 'im_message_receipt_status', 0, '对应 ImMessageReceiptStatusEnum', 'admin', '2026-04-30 11:35:07', 'admin', '2026-06-16 03:41:55', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2203, 'IM å¥½å‹çŠ¶æ€', 'im_friend_status', 0, '0=正常 / 1=已删除', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2204, 'IM ç¾¤çŠ¶æ€', 'im_group_status', 0, '0=正常 / 1=已解散', 'admin', '2026-04-30 11:35:07', 'admin', '2026-04-30 11:35:07', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2205, 'IM ç¾¤èŠæ¶ˆæ¯çŠ¶æ€', 'im_group_message_status', 0, '对应 ImMessageStatusEnum;群聊 0=正常 / 2=已撤回(无未读概念)', 'admin', '2026-04-30 15:14:36', 'admin', '2026-04-30 15:14:36', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2205, 'IM æ¶ˆæ¯çŠ¶æ€', 'im_message_status', 0, '对应 ImMessageStatusEnum;0=正常 / 2=已撤回(私聊 / ç¾¤èŠå…±ç”¨ï¼‰', 'admin', '2026-04-30 15:14:36', 'admin', '2026-06-16 03:41:55', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2206, 'IM ç¾¤æˆå‘˜è§’色', 'im_group_member_role', 0, NULL, '1', '2026-05-02 02:14:12', '1', '2026-05-02 02:14:12', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (2207, 'IM å¥½å‹æ·»åŠ æ¥æº', 'im_friend_add_source', 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 (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);
@@ -1682,7 +1719,13 @@
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);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061092, 'IM é€šè¯åª’体类型', 'im_rtc_call_media_type', 0, NULL, 'admin', '2026-05-16 11:34:50', 'admin', '2026-05-16 11:34:50', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061093, 'IM é€šè¯ä¼šè¯ç±»åž‹', 'im_rtc_call_conversation_type', 0, '1=私聊;2=群聊', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061094, 'IM é€šè¯çŠ¶æ€', 'im_rtc_call_status', 0, '10=创建;20=进行中;30=已结束', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061095, 'IM é€šè¯ç»“束原因', 'im_rtc_call_end_reason', 0, '1=通话结束;2=已拒绝;3=已取消;4=无人接听;5=对方正忙;9=通话异常', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061096, 'IM é€šè¯å‚与角色', 'im_rtc_participant_role', 0, '1=发起人;2=被邀请者;3=主动加入者', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061097, 'IM é€šè¯å‚与状态', 'im_rtc_participant_status', 0, '10=邀请中;20=已加入;30=已拒绝;40=未应答;50=已离开', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36:12', b'0', NULL);
INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`) VALUES (1061098, 'IM é¢‘道素材内容类型', 'im_channel_material_type', 0, '1=站内富文本 / 2=外链', '1', '2026-05-19 14:09:25', '1', '2026-05-19 14:09:25', b'0', NULL);
COMMIT;
-- ----------------------------
@@ -1708,7 +1751,7 @@
  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 = 4557 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
) ENGINE = InnoDB AUTO_INCREMENT = 5367 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '系统访问记录';
-- ----------------------------
-- Records of system_login_log
@@ -1841,7 +1884,7 @@
  `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 = 6611 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
) ENGINE = InnoDB AUTO_INCREMENT = 6735 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '菜单权限表';
-- ----------------------------
-- Records of system_menu
@@ -2186,7 +2229,7 @@
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2156, '查询项目', 'report:go-view-project:query', 3, 0, 2153, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:25:53', '1', '2023-02-07 19:25:53', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2157, '使用 SQL æŸ¥è¯¢æ•°æ®', 'report:go-view-data:get-by-sql', 3, 3, 2153, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:26:15', '1', '2023-02-07 19:26: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 (2158, '使用 HTTP æŸ¥è¯¢æ•°æ®', 'report:go-view-data:get-by-http', 3, 4, 2153, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2023-02-07 19:26:35', '1', '2023-02-07 19:26: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 (2159, 'Boot å¼€å‘文档', '', 1, 1, 0, 'https://doc.iocoder.cn/', 'ep:document', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:46:28', '1', '2026-01-05 19:31: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 (2159, 'Boot å¼€å‘文档', '', 1, 1, 0, 'https://doc.iocoder.cn/', 'ep:document', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:46:28', '1', '2026-06-12 00:05:19', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2160, 'Cloud å¼€å‘文档', '', 1, 2, 0, 'https://cloud.iocoder.cn', 'ep:document-copy', NULL, NULL, 0, b'1', b'1', b'1', '1', '2023-02-10 22:47:07', '1', '2023-12-02 21:32:29', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2161, '接入示例', '', 1, 99, 1117, 'demo', 'fa-solid:dragon', 'pay/demo/index', NULL, 0, b'1', b'1', b'1', '', '2023-02-11 14:21:42', '1', '2024-01-18 23:50:00', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (2162, '商品导出', 'product:spu:export', 3, 5, 2014, '', '', '', NULL, 0, b'1', b'1', b'1', '', '2022-07-30 14:22:58', '', '2022-07-30 14:22:58', b'0');
@@ -2773,7 +2816,7 @@
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5034, 'OTA å›ºä»¶åˆ›å»º', 'iot:ota-firmware:create', 3, 2, 5032, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:21', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5035, 'OTA å›ºä»¶æ›´æ–°', 'iot:ota-firmware:update', 3, 3, 5032, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:29', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5036, 'OTA å›ºä»¶åˆ é™¤', 'iot:ota-firmware:delete', 3, 4, 5032, '', '', '', '', 0, b'1', b'1', b'1', '', '2025-06-30 07:50:29', '\"1\"', '2025-06-30 17:38:37', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5037, 'OTA å‡çº§ä»»åŠ¡æŸ¥è¯¢', 'iot:ota-task:create', 3, 11, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:56:56', '1', '2025-07-02 23:56:56', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5037, 'OTA å‡çº§ä»»åŠ¡æŸ¥è¯¢', 'iot:ota-task:query', 3, 11, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:56:56', '1', '2026-05-19 08:48:53', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5038, 'OTA å‡çº§ä»»åŠ¡å–æ¶ˆ', 'iot:ota-task:cancel', 3, 13, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:57:26', '1', '2025-07-02 23:57:26', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (5039, 'OTA å‡çº§ä»»åŠ¡åˆ›å»º', 'iot:ota-task:create', 3, 12, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:57:52', '1', '2025-07-02 23:57: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 (5040, 'OTA å‡çº§è®°å½•查询', 'iot:ota-task-record:query', 3, 21, 5032, '', '', '', '', 0, b'1', b'1', b'1', '1', '2025-07-02 23:58:30', '1', '2025-07-02 23:58:30', b'0');
@@ -3270,6 +3313,24 @@
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6582, '用户表情删除', 'im:manager:face-user-item:delete', 3, 20, 6580, '', '', '', '', 0, b'1', b'1', b'1', '', '2026-05-06 13:56:08', '', '2026-05-06 13:56:08', b'0');
INSERT INTO `system_menu` (`id`, `name`, `permission`, `type`, `sort`, `parent_id`, `path`, `icon`, `component`, `component_name`, `status`, `visible`, `keep_alive`, `always_show`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (6600, '私聊管理', '', 1, 20, 6500, 'private', 'ep:chat-round', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-07 00:40:35', '1', '2026-05-07 00:40: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 (6610, '群聊管理', '', 1, 30, 6500, 'group', 'ep:chat-line-round', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-07 00:40:35', '1', '2026-05-07 00:40: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 (6611, '通话记录', '', 2, 40, 6500, 'rtc', 'ep:phone', 'im/manager/rtc/index', 'ImRtcCall', 0, b'1', b'1', b'1', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36: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 (6612, '通话记录查询', 'im:manager:rtc:query', 3, 1, 6611, '', '', NULL, NULL, 0, b'1', b'1', b'1', 'admin', '2026-05-18 03:36:12', 'admin', '2026-05-18 03:36: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 (6700, '频道管理', '', 1, 90, 6500, 'channel', 'ep:promotion', NULL, NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6710, '频道列表', '', 2, 1, 6700, 'list', 'ep:promotion', 'im/manager/channel/list/index', 'ImChannel', 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-19 09:44: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 (6711, '频道查询', 'im:manager:channel:query', 3, 1, 6710, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6712, '频道创建', 'im:manager:channel:create', 3, 2, 6710, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6713, '频道修改', 'im:manager:channel:update', 3, 3, 6710, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6714, '频道删除', 'im:manager:channel:delete', 3, 4, 6710, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6720, '频道素材', '', 2, 2, 6700, 'material', 'ep:document', 'im/manager/channel/material/index', 'ImChannelMaterial', 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-19 09:44: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 (6721, '素材查询', 'im:manager:channel-material:query', 3, 1, 6720, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6722, '素材创建', 'im:manager:channel-material:create', 3, 2, 6720, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6723, '素材修改', 'im:manager:channel-material:update', 3, 3, 6720, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6724, '素材删除', 'im:manager:channel-material:delete', 3, 4, 6720, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6730, '频道消息', '', 2, 3, 6700, 'message', 'ep:message', 'im/manager/channel/message/index', 'ImChannelMessage', 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-19 09:44: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 (6731, '消息查询', 'im:manager:channel-message:query', 3, 1, 6730, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6732, '立即推送', 'im:manager:channel-message:send', 3, 2, 6730, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6733, '消息删除', 'im:manager:channel-message:delete', 3, 3, 6730, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-18 13:14:34', '1', '2026-05-18 13:14: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 (6734, '解散群', 'im:manager:group:dissolve', 3, 21, 6540, '', '', '', NULL, 0, b'1', b'1', b'1', '1', '2026-05-24 12:02:38', '1', '2026-05-24 12:02:38', b'0');
COMMIT;
-- ----------------------------
@@ -3392,7 +3453,7 @@
  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 = 54457 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 è®¿é—®ä»¤ç‰Œ';
) ENGINE = InnoDB AUTO_INCREMENT = 108473 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 è®¿é—®ä»¤ç‰Œ';
-- ----------------------------
-- Records of system_oauth2_access_token
@@ -3465,7 +3526,7 @@
INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, 'default', 'admin123', '芋道源码', 'http://test.yudao.iocoder.cn/20250502/sort2_1746189740718.png', '我是描述', 0, 1800, 2592000, '[\"https://www.iocoder.cn\",\"https://doc.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\",\"refresh_token\",\"client_credentials\"]', '[\"user.read\",\"user.write\"]', '[]', '[\"user.read\",\"user.write\"]', '[]', '{}', '1', '2022-05-11 21:47:12', '1', '2025-12-07 20:07:09', b'0');
INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (40, 'test', 'test2', 'biubiu', 'http://test.yudao.iocoder.cn/20251227/javayuanma_1766829882970.jpg', '啦啦啦啦', 0, 1800, 43200, '[\"https://www.iocoder.cn\"]', '[\"password\",\"authorization_code\",\"implicit\"]', '[\"user_info\",\"projects\"]', '[\"user_info\"]', '[]', '[]', '{}', '1', '2022-05-12 00:28:20', '1', '2025-12-27 18:04:44', b'0');
INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (41, 'yudao-sso-demo-by-code', 'test', '基于授权码模式,如何实现 SSO å•点登录?', 'http://test.yudao.iocoder.cn/it/20250502/sign_1746181948685.png', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"authorization_code\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-09-29 13:28:31', '1', '2025-05-02 18:32:30', b'0');
INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (42, 'yudao-sso-demo-by-password', 'test', '基于密码模式,如何实现 SSO å•点登录?', 'http://test.yudao.iocoder.cn/20251025/images (3)_1761360515810.jpeg', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"password\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-10-04 17:40:16', '1', '2025-10-25 10:49:40', b'0');
INSERT INTO `system_oauth2_client` (`id`, `client_id`, `secret`, `name`, `logo`, `description`, `status`, `access_token_validity_seconds`, `refresh_token_validity_seconds`, `redirect_uris`, `authorized_grant_types`, `scopes`, `auto_approve_scopes`, `authorities`, `resource_ids`, `additional_information`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (42, 'yudao-sso-demo-by-password', 'test', '基于密码模式,如何实现 SSO å•点登录?', 'http://test.yudao.iocoder.cn/oauth2/20260615/iShot_2026-05-31_23.18.10.png', NULL, 0, 1800, 43200, '[\"http://127.0.0.1:18080\"]', '[\"password\",\"refresh_token\"]', '[\"user.read\",\"user.write\"]', '[]', '[]', '[]', NULL, '1', '2022-10-04 17:40:16', '1', '2026-06-15 18:58:30', b'0');
COMMIT;
-- ----------------------------
@@ -3518,7 +3579,7 @@
  `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 = 2590 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 åˆ·æ–°ä»¤ç‰Œ';
) ENGINE = InnoDB AUTO_INCREMENT = 3382 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = 'OAuth2 åˆ·æ–°ä»¤ç‰Œ';
-- ----------------------------
-- Records of system_oauth2_refresh_token
@@ -3554,7 +3615,7 @@
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_user_id`(`user_id` ASC) USING BTREE,
  INDEX `idx_create_time`(`create_time` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9194 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 ç‰ˆæœ¬';
) ENGINE = InnoDB AUTO_INCREMENT = 9208 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '操作日志记录 V2 ç‰ˆæœ¬';
-- ----------------------------
-- Records of system_operate_log
@@ -3580,7 +3641,7 @@
  `deleted` bit(1) NOT NULL DEFAULT b'0' COMMENT '是否删除',
  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 8 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '岗位信息表';
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '岗位信息表';
-- ----------------------------
-- Records of system_post
@@ -3624,7 +3685,7 @@
INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3, 'CRM ç®¡ç†å‘˜', 'crm_admin', 2, 1, '', 0, 1, 'CRM ä¸“属角色', '1', '2024-02-24 10:51:13', '1', '2024-02-24 02:51:32', b'0', 1);
INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (109, '租户管理员', 'tenant_admin', 0, 1, '', 0, 1, '系统自动生成', '1', '2022-02-22 00:56:14', '1', '2022-02-22 00:56:14', b'0', 121);
INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (111, '租户管理员', 'tenant_admin', 0, 1, '', 0, 1, '系统自动生成', '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);
INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (155, '测试数据权限1', 'test-dp', 4, 2, '[112,100,102,103,104,105,107,108]', 0, 2, '1111', '1', '2025-03-31 14:58:06', '1', '2025-12-04 23:29:40', b'0', 1);
INSERT INTO `system_role` (`id`, `name`, `code`, `sort`, `data_scope`, `data_scope_dept_ids`, `status`, `type`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (155, '测试数据权限12', 'test-dp', 4, 2, '[112,100,102,103,104,105,107,108]', 0, 2, '1111', '1', '2025-03-31 14:58:06', '1', '2026-06-26 10:42:44', b'0', 1);
COMMIT;
-- ----------------------------
@@ -3643,7 +3704,7 @@
  `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 = 6381 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
) ENGINE = InnoDB AUTO_INCREMENT = 6692 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '角色和菜单关联表';
-- ----------------------------
-- Records of system_role_menu
@@ -3683,7 +3744,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (706, 2, 112, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (707, 2, 113, '1', '2022-02-22 13:16:57', '1', '2022-02-22 13:16:57', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1296, 110, 1, '110', '2022-02-23 00:23:55', '110', '2022-02-23 00:23:55', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1578, 111, 1, '1', '2022-03-07 21:37:58', '1', '2022-03-07 21:37:58', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1729, 109, 100, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1730, 109, 101, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1731, 109, 1063, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
@@ -3701,23 +3761,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1743, 109, 1010, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1744, 109, 1011, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1745, 109, 1012, '1', '2022-09-21 22:08:51', '1', '2022-09-21 22:08:51', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1746, 111, 100, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1747, 111, 101, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1748, 111, 1063, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1749, 111, 1064, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1750, 111, 1001, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1751, 111, 1065, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1752, 111, 1002, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1753, 111, 1003, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1754, 111, 1004, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1755, 111, 1005, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1756, 111, 1006, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1757, 111, 1007, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1758, 111, 1008, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1759, 111, 1009, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1760, 111, 1010, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1761, 111, 1011, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1762, 111, 1012, '1', '2022-09-21 22:08:52', '1', '2022-09-21 22:08:52', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1763, 109, 100, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1764, 109, 101, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1765, 109, 1063, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);
@@ -3735,23 +3778,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1777, 109, 1010, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1778, 109, 1011, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1779, 109, 1012, '1', '2022-09-21 22:08:53', '1', '2022-09-21 22:08:53', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1780, 111, 100, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1781, 111, 101, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1782, 111, 1063, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1783, 111, 1064, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1784, 111, 1001, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1785, 111, 1065, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1786, 111, 1002, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1787, 111, 1003, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1788, 111, 1004, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1789, 111, 1005, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1790, 111, 1006, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1791, 111, 1007, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1792, 111, 1008, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1793, 111, 1009, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1794, 111, 1010, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1795, 111, 1011, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1796, 111, 1012, '1', '2022-09-21 22:08:54', '1', '2022-09-21 22:08:54', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1797, 109, 100, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1798, 109, 101, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1799, 109, 1063, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);
@@ -3769,43 +3795,16 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1811, 109, 1010, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1812, 109, 1011, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1813, 109, 1012, '1', '2022-09-21 22:08:55', '1', '2022-09-21 22:08:55', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1814, 111, 100, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1815, 111, 101, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1816, 111, 1063, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1817, 111, 1064, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1818, 111, 1001, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1819, 111, 1065, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1820, 111, 1002, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1821, 111, 1003, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1822, 111, 1004, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1823, 111, 1005, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1824, 111, 1006, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1825, 111, 1007, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1826, 111, 1008, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1827, 111, 1009, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1828, 111, 1010, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1829, 111, 1011, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1830, 111, 1012, '1', '2022-09-21 22:08:56', '1', '2022-09-21 22:08:56', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1831, 109, 103, '1', '2022-09-21 22:43:23', '1', '2022-09-21 22:43:23', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1832, 109, 1017, '1', '2022-09-21 22:43:23', '1', '2022-09-21 22:43:23', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1833, 109, 1018, '1', '2022-09-21 22:43:23', '1', '2022-09-21 22:43:23', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1834, 109, 1019, '1', '2022-09-21 22:43:23', '1', '2022-09-21 22:43:23', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1835, 109, 1020, '1', '2022-09-21 22:43:23', '1', '2022-09-21 22:43:23', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1836, 111, 103, '1', '2022-09-21 22:43:24', '1', '2022-09-21 22:43:24', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1837, 111, 1017, '1', '2022-09-21 22:43:24', '1', '2022-09-21 22:43:24', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1838, 111, 1018, '1', '2022-09-21 22:43:24', '1', '2022-09-21 22:43:24', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1839, 111, 1019, '1', '2022-09-21 22:43:24', '1', '2022-09-21 22:43:24', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1840, 111, 1020, '1', '2022-09-21 22:43:24', '1', '2022-09-21 22:43:24', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1841, 109, 1036, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1842, 109, 1037, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1843, 109, 1038, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1844, 109, 1039, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1845, 109, 107, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1846, 111, 1036, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1847, 111, 1037, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1848, 111, 1038, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1849, 111, 1039, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1850, 111, 107, '1', '2022-09-21 22:48:13', '1', '2022-09-21 22:48:13', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1991, 2, 1024, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1992, 2, 1025, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (1993, 2, 1026, '1', '2023-01-25 08:42:52', '1', '2023-01-25 08:42:52', b'0', 1);
@@ -3975,18 +3974,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2938, 109, 1141, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2939, 109, 1142, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2940, 109, 1143, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2941, 111, 1224, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2942, 111, 1225, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2943, 111, 1226, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2944, 111, 1227, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2945, 111, 1228, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2946, 111, 1229, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2947, 111, 1138, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2948, 111, 1139, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2949, 111, 1140, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2950, 111, 1141, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2951, 111, 1142, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2952, 111, 1143, '1', '2023-12-02 23:19:40', '1', '2023-12-02 23:19:40', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2993, 109, 2, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2994, 109, 1031, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2995, 109, 1032, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
@@ -4062,91 +4049,11 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3066, 109, 1258, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3067, 109, 1259, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3068, 109, 1260, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3069, 111, 2, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3070, 111, 1031, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3071, 111, 1032, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3072, 111, 1033, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3073, 111, 1034, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3074, 111, 1035, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3075, 111, 1050, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3076, 111, 1051, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3077, 111, 1052, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3078, 111, 1053, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3079, 111, 1054, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3080, 111, 1056, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3081, 111, 1057, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3082, 111, 1058, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3083, 111, 1059, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3084, 111, 1060, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3085, 111, 1066, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3086, 111, 1067, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3087, 111, 1070, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3088, 111, 1075, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3090, 111, 1077, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3091, 111, 1078, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3092, 111, 1082, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3093, 111, 1083, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3094, 111, 1084, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3095, 111, 1085, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3096, 111, 1086, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3097, 111, 1087, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3098, 111, 1088, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3099, 111, 1089, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3100, 111, 1090, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3101, 111, 1091, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3102, 111, 1092, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3103, 111, 106, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3104, 111, 110, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3105, 111, 111, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3106, 111, 112, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3107, 111, 113, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3108, 111, 114, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3109, 111, 115, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3110, 111, 116, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3111, 111, 2472, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3112, 111, 2478, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3113, 111, 2479, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3114, 111, 2480, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3115, 111, 2481, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3116, 111, 2482, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3117, 111, 2483, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3118, 111, 2484, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3119, 111, 2485, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3120, 111, 2486, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3121, 111, 2487, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3122, 111, 2488, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3123, 111, 2489, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3124, 111, 2490, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3125, 111, 2491, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3126, 111, 2492, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3127, 111, 2493, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3128, 111, 2494, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3129, 111, 2495, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3130, 111, 2497, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3131, 111, 1237, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3132, 111, 1238, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3133, 111, 1239, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3134, 111, 1240, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3135, 111, 1241, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3136, 111, 1242, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3137, 111, 1243, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3138, 111, 2525, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3139, 111, 1255, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3140, 111, 1256, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3141, 111, 1257, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3142, 111, 1258, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3143, 111, 1259, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3144, 111, 1260, '1', '2023-12-02 23:41:02', '1', '2023-12-02 23:41:02', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3221, 109, 102, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3222, 109, 1013, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3223, 109, 1014, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3224, 109, 1015, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3225, 109, 1016, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3226, 111, 102, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3227, 111, 1013, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3228, 111, 1014, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3229, 111, 1015, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (3230, 111, 1016, '1', '2023-12-30 11:42:36', '1', '2023-12-30 11:42:36', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4163, 109, 5, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4164, 109, 1118, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4165, 109, 1119, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
@@ -4205,64 +4112,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4219, 109, 1220, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4220, 109, 1221, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4221, 109, 1222, '1', '2024-03-30 17:53:17', '1', '2024-03-30 17:53:17', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4222, 111, 5, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4223, 111, 1118, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4224, 111, 1119, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4225, 111, 1120, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4226, 111, 2713, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4227, 111, 2714, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4228, 111, 2715, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4229, 111, 2716, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4230, 111, 2717, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4231, 111, 2718, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4232, 111, 2720, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4233, 111, 1185, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4234, 111, 2721, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4235, 111, 1186, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4236, 111, 2722, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4237, 111, 1187, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4238, 111, 2723, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4239, 111, 1188, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4240, 111, 2724, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4241, 111, 1189, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4242, 111, 2725, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4243, 111, 1190, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4244, 111, 2726, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4245, 111, 1191, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4246, 111, 2727, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4247, 111, 1192, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4248, 111, 2728, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4249, 111, 1193, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4250, 111, 2729, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4251, 111, 1194, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4252, 111, 2730, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4253, 111, 1195, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4254, 111, 2731, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4256, 111, 2732, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4257, 111, 1197, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4258, 111, 2733, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4259, 111, 1198, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4260, 111, 2734, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4261, 111, 1199, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4262, 111, 2735, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4263, 111, 1200, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4264, 111, 1201, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4265, 111, 1202, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4266, 111, 1207, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4267, 111, 1208, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4268, 111, 1209, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4269, 111, 1210, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4270, 111, 1211, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4271, 111, 1212, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4272, 111, 1213, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4273, 111, 1215, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4274, 111, 1216, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4275, 111, 1217, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4276, 111, 1218, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4277, 111, 1219, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4278, 111, 1220, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4279, 111, 1221, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (4280, 111, 1222, '1', '2024-03-30 17:53:18', '1', '2024-03-30 17:53:18', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5779, 2, 2739, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5780, 2, 2740, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5781, 2, 2758, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
@@ -4272,8 +4121,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5785, 2, 2030, '1', '2024-07-07 20:39:38', '1', '2024-07-07 20:39:38', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5789, 109, 2739, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5790, 109, 2740, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5791, 111, 2739, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5792, 111, 2740, '1', '2024-07-13 22:37:24', '1', '2024-07-13 22:37:24', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6053, 155, 4000, '1', '2025-04-01 13:48:26', '1', '2025-04-01 13:48:26', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6097, 155, 4050, '1', '2025-04-01 13:48:26', '1', '2025-04-01 13:48:26', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6104, 155, 4032, '1', '2025-04-01 13:49:30', '1', '2025-04-01 13:49:30', b'0', 1);
@@ -4348,45 +4195,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6175, 109, 2557, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6176, 109, 2558, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6177, 109, 2559, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6178, 111, 1117, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6179, 111, 1126, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6180, 111, 1127, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6181, 111, 1128, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6182, 111, 1129, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6183, 111, 1130, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6184, 111, 1132, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6185, 111, 1133, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6186, 111, 1134, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6187, 111, 1135, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6188, 111, 1136, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6189, 111, 1137, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6190, 111, 2161, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6191, 111, 1150, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6192, 111, 1161, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6193, 111, 1162, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6194, 111, 1166, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6195, 111, 1173, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6196, 111, 1174, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6197, 111, 1178, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6198, 111, 2745, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6199, 111, 2746, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6200, 111, 2747, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6201, 111, 2748, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6202, 111, 2301, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6203, 111, 2302, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6204, 111, 5011, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6205, 111, 5012, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6206, 111, 2549, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6207, 111, 2550, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6208, 111, 2551, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6209, 111, 2552, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6210, 111, 2553, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6211, 111, 2554, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6212, 111, 2555, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6213, 111, 2556, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6214, 111, 2557, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6215, 111, 2558, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6216, 111, 2559, '1', '2025-09-06 20:52:12', '1', '2025-09-06 20:52:12', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6217, 109, 2756, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6218, 109, 2757, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6219, 109, 2262, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);
@@ -4425,44 +4233,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6252, 109, 2335, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6253, 109, 2363, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6254, 109, 2364, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6255, 111, 2756, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6256, 111, 2757, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6257, 111, 2262, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6258, 111, 2275, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6259, 111, 2276, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6260, 111, 2277, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6261, 111, 2281, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6262, 111, 2282, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6263, 111, 2283, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6264, 111, 2284, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6265, 111, 2285, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6266, 111, 2287, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6267, 111, 2288, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6268, 111, 2293, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6269, 111, 2294, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6270, 111, 2297, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6271, 111, 2300, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6272, 111, 2317, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6273, 111, 2318, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6274, 111, 2319, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6275, 111, 2320, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6276, 111, 2321, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6277, 111, 2322, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6278, 111, 2323, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6279, 111, 2324, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6280, 111, 2325, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6281, 111, 2326, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6282, 111, 2327, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6283, 111, 2328, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6284, 111, 2329, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6285, 111, 2330, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6286, 111, 2331, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6287, 111, 2332, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6288, 111, 2333, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6289, 111, 2334, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6290, 111, 2335, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6291, 111, 2363, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6292, 111, 2364, '1', '2025-09-06 20:52:25', '1', '2025-09-06 20:52:25', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6293, 2, 5, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6294, 2, 1118, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6295, 2, 1119, '1', '2026-01-04 18:09:41', '1', '2026-01-04 18:09:41', b'0', 1);
@@ -4533,7 +4303,6 @@
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6360, 2, 5811, '', '2026-03-30 03:13:05', '', '2026-03-30 03:13:05', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6361, 3, 5811, '', '2026-03-30 03:13:05', '', '2026-03-30 03:13:05', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6362, 109, 5811, '', '2026-03-30 03:13:05', '', '2026-03-30 03:13:05', b'0', 121);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6363, 111, 5811, '', '2026-03-30 03:13:05', '', '2026-03-30 03:13:05', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6364, 155, 5811, '', '2026-03-30 03:13:05', '', '2026-03-30 03:13:05', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6365, 2, 6400, '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 (6366, 2, 6401, '1', '2026-05-09 16:11:01', '1', '2026-05-09 16:11:01', b'0', 1);
@@ -4543,6 +4312,317 @@
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 (6380, 2, 6490, '1', '2026-05-14 09:36:12', '1', '2026-05-14 09:36:12', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6381, 111, 1, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6382, 111, 2, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6383, 111, 5, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6384, 111, 1031, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6385, 111, 1032, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6386, 111, 1033, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6387, 111, 1034, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6388, 111, 1035, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6389, 111, 1036, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6390, 111, 1037, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6391, 111, 1038, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6392, 111, 1039, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6393, 111, 1050, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6394, 111, 1051, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6395, 111, 1052, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6396, 111, 1053, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6397, 111, 1054, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6398, 111, 1056, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6399, 111, 1057, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6400, 111, 1058, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6401, 111, 1059, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6402, 111, 1060, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6403, 111, 1063, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6404, 111, 1064, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6405, 111, 1065, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6406, 111, 1066, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6407, 111, 1067, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6408, 111, 1070, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6409, 111, 1075, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6410, 111, 1077, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6411, 111, 1078, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6412, 111, 1082, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6413, 111, 1083, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6414, 111, 1084, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6415, 111, 1085, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6416, 111, 1086, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6417, 111, 1087, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6418, 111, 1088, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6419, 111, 1089, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6420, 111, 1090, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6421, 111, 1091, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6422, 111, 1092, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6423, 111, 1117, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6424, 111, 1118, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6425, 111, 1119, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6426, 111, 1120, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6427, 111, 100, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6428, 111, 101, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6429, 111, 102, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6430, 111, 1126, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6431, 111, 103, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6432, 111, 1127, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6433, 111, 1128, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6434, 111, 1129, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6435, 111, 106, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6436, 111, 1130, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6437, 111, 107, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6438, 111, 1132, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6439, 111, 1133, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6440, 111, 110, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6441, 111, 1134, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6442, 111, 111, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6443, 111, 1135, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6444, 111, 112, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6445, 111, 1136, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6446, 111, 113, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6447, 111, 1137, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6448, 111, 2161, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6449, 111, 114, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6450, 111, 1138, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6451, 111, 1139, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6452, 111, 115, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6453, 111, 1140, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6454, 111, 116, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6455, 111, 1141, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6456, 111, 1142, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6457, 111, 1143, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6458, 111, 1150, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6459, 111, 1161, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6460, 111, 1162, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6461, 111, 1166, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6462, 111, 1173, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6463, 111, 1174, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6464, 111, 2713, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6465, 111, 2714, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6466, 111, 1178, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6467, 111, 2715, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6468, 111, 2716, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6469, 111, 2717, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6470, 111, 2718, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6471, 111, 2720, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6472, 111, 2721, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6473, 111, 1185, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6474, 111, 2722, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6475, 111, 1186, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6476, 111, 1187, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6477, 111, 2723, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6478, 111, 1188, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6479, 111, 2724, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6480, 111, 1189, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6481, 111, 2725, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6482, 111, 1190, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6483, 111, 2726, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6484, 111, 1191, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6485, 111, 2727, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6486, 111, 1192, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6487, 111, 2728, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6488, 111, 2729, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6489, 111, 1193, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6490, 111, 1194, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6491, 111, 2730, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6492, 111, 1195, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6493, 111, 2731, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6494, 111, 2732, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6495, 111, 1197, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6496, 111, 2733, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6497, 111, 1198, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6498, 111, 2734, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6499, 111, 1199, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6500, 111, 2735, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6501, 111, 1200, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6502, 111, 1201, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6503, 111, 1202, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6504, 111, 2739, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6505, 111, 2740, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6506, 111, 1207, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6507, 111, 1208, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6508, 111, 1209, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6509, 111, 2745, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6510, 111, 1210, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6511, 111, 2746, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6512, 111, 1211, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6513, 111, 2747, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6514, 111, 1212, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6515, 111, 2748, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6516, 111, 1213, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6517, 111, 1215, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6518, 111, 1216, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6519, 111, 1217, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6520, 111, 1218, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6521, 111, 1219, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6522, 111, 1220, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6523, 111, 2756, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6524, 111, 1221, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6525, 111, 2757, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6526, 111, 1222, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6527, 111, 1224, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6528, 111, 1225, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6529, 111, 1226, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6530, 111, 1227, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6531, 111, 1228, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6532, 111, 1229, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6533, 111, 1237, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6534, 111, 1238, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6535, 111, 2262, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6536, 111, 1239, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6537, 111, 1240, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6538, 111, 1241, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6539, 111, 1242, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6540, 111, 1243, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6541, 111, 2275, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6542, 111, 2276, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6543, 111, 2277, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6544, 111, 1255, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6545, 111, 1256, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6546, 111, 1257, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6547, 111, 2281, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6548, 111, 1258, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6549, 111, 2282, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6550, 111, 1259, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6551, 111, 2283, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6552, 111, 1260, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6553, 111, 2284, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6554, 111, 2285, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6555, 111, 2287, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6556, 111, 2288, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6557, 111, 2293, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6558, 111, 2294, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6559, 111, 2297, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6560, 111, 2300, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6561, 111, 2301, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6562, 111, 2302, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6563, 111, 2317, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6564, 111, 2318, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6565, 111, 2319, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6566, 111, 2320, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6567, 111, 2321, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6568, 111, 2322, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6569, 111, 2323, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6570, 111, 2324, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6571, 111, 2325, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6572, 111, 2326, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6573, 111, 2327, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6574, 111, 2328, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6575, 111, 2329, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6576, 111, 2330, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6577, 111, 2331, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6578, 111, 2332, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6579, 111, 2333, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6580, 111, 2334, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6581, 111, 2335, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6582, 111, 2363, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6583, 111, 2364, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6584, 111, 5011, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6585, 111, 5012, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6586, 111, 2472, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6587, 111, 2478, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6588, 111, 2479, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6589, 111, 2480, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6590, 111, 2481, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6591, 111, 2482, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6592, 111, 2483, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6593, 111, 2484, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6594, 111, 2485, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6595, 111, 2486, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6596, 111, 2487, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6597, 111, 2488, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6598, 111, 2489, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6599, 111, 2490, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6600, 111, 2491, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6601, 111, 2492, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6602, 111, 2493, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6603, 111, 2494, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6604, 111, 2495, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6605, 111, 2497, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6606, 111, 2525, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6607, 111, 1001, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6608, 111, 1002, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6609, 111, 1003, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6610, 111, 1004, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6611, 111, 1005, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6612, 111, 1006, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6613, 111, 1007, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6614, 111, 1008, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6615, 111, 1009, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6616, 111, 1010, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6617, 111, 1011, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6618, 111, 1012, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6619, 111, 1013, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6620, 111, 2549, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6621, 111, 1014, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6622, 111, 2550, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6623, 111, 1015, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6624, 111, 2551, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6625, 111, 1016, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6626, 111, 2552, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6627, 111, 1017, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6628, 111, 2553, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6629, 111, 1018, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6630, 111, 2554, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6631, 111, 1019, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6632, 111, 2555, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6633, 111, 1020, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6634, 111, 2556, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6635, 111, 2557, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6636, 111, 2558, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6637, 111, 2559, '1', '2026-06-01 23:29:35', '1', '2026-06-01 23:29:35', b'0', 122);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6638, 155, 5780, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6639, 155, 2084, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6640, 155, 2085, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6641, 155, 2086, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6642, 155, 2087, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6643, 155, 2088, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6644, 155, 2089, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6645, 155, 2090, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6646, 155, 2091, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6647, 155, 2092, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6648, 155, 2093, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6649, 155, 5805, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6650, 155, 2094, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6651, 155, 2095, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6652, 155, 2096, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6653, 155, 2097, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6654, 155, 2098, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6655, 155, 5042, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6656, 155, 2099, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6657, 155, 5043, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6658, 155, 2100, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6659, 155, 5044, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6660, 155, 2101, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6661, 155, 5045, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6662, 155, 2102, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6663, 155, 5046, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6664, 155, 2103, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6665, 155, 2104, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6666, 155, 2105, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6667, 155, 2106, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6668, 155, 2107, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6669, 155, 2108, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6670, 155, 2109, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6671, 155, 2110, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6672, 155, 2111, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6673, 155, 2112, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6674, 155, 2113, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6675, 155, 2114, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6676, 155, 2115, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6677, 155, 2116, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6678, 155, 2117, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6679, 155, 2118, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6680, 155, 2119, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6681, 155, 2120, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6682, 155, 2121, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6683, 155, 2122, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6684, 155, 2123, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6685, 155, 2124, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6686, 155, 2125, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6687, 155, 2126, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6688, 155, 2127, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6689, 155, 2128, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6690, 155, 2129, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
INSERT INTO `system_role_menu` (`id`, `role_id`, `menu_id`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (6691, 155, 5100, '1', '2026-06-10 09:04:18', '1', '2026-06-10 09:04:18', b'0', 1);
COMMIT;
-- ----------------------------
@@ -4597,7 +4677,7 @@
  `tenant_id` bigint NOT NULL DEFAULT 0 COMMENT '租户编号',
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `idx_mobile`(`mobile` ASC) USING BTREE COMMENT '手机号'
) ENGINE = InnoDB AUTO_INCREMENT = 690 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
) ENGINE = InnoDB AUTO_INCREMENT = 694 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '手机验证码';
-- ----------------------------
-- Records of system_sms_code
@@ -4638,7 +4718,7 @@
  `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 = 1553 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
) ENGINE = InnoDB AUTO_INCREMENT = 1568 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '短信日志';
-- ----------------------------
-- Records of system_sms_log
@@ -4815,7 +4895,7 @@
BEGIN;
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (1, '芋道源码', NULL, '芋艿', '17321315478', 0, 'www.iocoder.cn,127.0.0.1:3000,wxc4598c446f8a9cb3', 0, '2099-02-19 17:14:16', 9999, '1', '2021-01-05 17:03:47', '1', '2025-08-19 05:18:41', b'0');
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (121, '小租户', 110, '小王2', '15601691300', 0, 'zsxq.iocoder.cn,123321', 111, '2026-07-10 00:00:00', 30, '1', '2022-02-22 00:56:14', '1', '2025-08-19 21:19:29', b'0');
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn,222,333', 111, '2023-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2025-12-21 09:50:00', b'0');
INSERT INTO `system_tenant` (`id`, `name`, `contact_user_id`, `contact_name`, `contact_mobile`, `status`, `websites`, `package_id`, `expire_time`, `account_count`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (122, '测试租户', 113, '芋道', '15601691300', 0, 'test.iocoder.cn,222,333', 111, '2023-04-29 00:00:00', 50, '1', '2022-03-07 21:37:58', '1', '2026-06-01 23:29:35', b'0');
COMMIT;
-- ----------------------------
@@ -4834,13 +4914,14 @@
  `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 = 113 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '租户套餐表';
) ENGINE = InnoDB AUTO_INCREMENT = 114 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '租户套餐表';
-- ----------------------------
-- Records of system_tenant_package
-- ----------------------------
BEGIN;
INSERT INTO `system_tenant_package` (`id`, `name`, `status`, `remark`, `menu_ids`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (111, '普通套餐', 0, '小功能', '[1,2,5,1031,1032,1033,1034,1035,1036,1037,1038,1039,1050,1051,1052,1053,1054,1056,1057,1058,1059,1060,1063,1064,1065,1066,1067,1070,1075,1077,1078,1082,1083,1084,1085,1086,1087,1088,1089,1090,1091,1092,1117,1118,1119,1120,100,101,102,1126,103,1127,1128,1129,106,1130,107,1132,1133,110,1134,111,1135,112,1136,113,1137,2161,114,1138,1139,115,1140,116,1141,1142,1143,1150,1161,1162,1166,1173,1174,2713,2714,1178,2715,2716,2717,2718,2720,2721,1185,2722,1186,1187,2723,1188,2724,1189,2725,1190,2726,1191,2727,1192,2728,2729,1193,1194,2730,1195,2731,2732,1197,2733,1198,2734,1199,2735,1200,1201,1202,2739,2740,1207,1208,1209,2745,1210,2746,1211,2747,1212,2748,1213,1215,1216,1217,1218,1219,1220,2756,1221,2757,1222,1224,1225,1226,1227,1228,1229,1237,1238,2262,1239,1240,1241,1242,1243,2275,2276,2277,1255,1256,1257,2281,1258,2282,1259,2283,1260,2284,2285,2287,2288,2293,2294,2297,2300,2301,2302,2317,2318,2319,2320,2321,2322,2323,2324,2325,2326,2327,2328,2329,2330,2331,2332,2333,2334,2335,2363,2364,5011,5012,2472,2478,2479,2480,2481,2482,2483,2484,2485,2486,2487,2488,2489,2490,2491,2492,2493,2494,2495,2497,2525,1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1011,1012,1013,2549,1014,2550,1015,2551,1016,2552,1017,2553,1018,2554,1019,2555,1020,2556,2557,2558,2559]', '1', '2022-02-22 00:54:00', '1', '2025-09-06 20:52:25', b'0');
INSERT INTO `system_tenant_package` (`id`, `name`, `status`, `remark`, `menu_ids`, `creator`, `create_time`, `updater`, `update_time`, `deleted`) VALUES (113, '测试套餐(啥都没有)', 0, '', '[2160,1254,2159]', '1', '2026-06-01 23:22:39', '1', '2026-06-09 14:44:00', b'0');
COMMIT;
-- ----------------------------
@@ -4952,32 +5033,32 @@
  INDEX `idx_mobile`(`mobile` ASC) USING BTREE,
  INDEX `idx_email`(`email` ASC) USING BTREE,
  INDEX `idx_dept_id`(`dept_id` ASC) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 145 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户信息表';
) ENGINE = InnoDB AUTO_INCREMENT = 225 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_unicode_ci COMMENT = '用户信息表';
-- ----------------------------
-- 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-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 (1, 'admin', '$2a$04$.vd8nPeLwxt6hnSzmAoAyul8BOLX7Cib6QhcxRe30rfvrIPQHH1OG', '芋道源码', '管理员', 103, '[1,2]', '13aoteman@126.com', '18818260272', 1, 'http://test.yudao.iocoder.cn/20260517/blob_1778998103688.png', 0, '0:0:0:0:0:0:0:1', '2026-06-28 03:39:52', 'admin', '2021-01-05 17:03:47', NULL, '2026-06-28 03:39:52', b'0', 1);
INSERT INTO `system_users` (`id`, `username`, `password`, `nickname`, `remark`, `dept_id`, `post_ids`, `email`, `mobile`, `sex`, `avatar`, `status`, `login_ip`, `login_date`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (100, 'yudao', '$2a$04$.vd8nPeLwxt6hnSzmAoAyul8BOLX7Cib6QhcxRe30rfvrIPQHH1OG', '芋道', '不要吓我', 104, '[1]', 'yudao@iocoder.cn', '15601691300', 1, NULL, 0, '127.0.0.1', '2026-06-17 09:37:17', '', '2021-01-07 09:07:17', NULL, '2026-06-17 09:37:17', 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);
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-06-16 11:37:56', '', '2021-01-21 02:13:53', NULL, '2026-06-16 11:37:56', 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 (107, 'admin107', '$2a$10$dYOOBKMO93v/.ReCqzyFg.o67Tqk.bbc2bhrpyBGkIw9aypCtr2pm', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 22:59:33', '1', '2025-04-21 14:23:08', b'0', 118);
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 (108, 'admin108', '$2a$10$y6mfvKoNYL1GXWak8nYwVOH.kCWqjactkzdoIDgiKl93WN3Ejg.Lu', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 23:00:50', '1', '2025-04-21 14:23:08', b'0', 119);
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 (109, 'admin109', '$2a$10$JAqvH0tEc0I7dfDVBI7zyuB4E3j.uH6daIjV53.vUS6PknFkDJkuK', '芋艿', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '', NULL, '1', '2022-02-20 23:11:50', '1', '2025-04-21 14:23:08', b'0', 120);
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 (110, 'admin110', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '小王', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2024-07-20 22:23:17', '1', '2022-02-22 00:56:14', NULL, '2025-04-21 14:23:08', b'0', 121);
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 (111, 'test', '$2a$10$mRMIYLDtRHlf6.9ipiqH1.Z.bh/R9dO9d5iHiGYPigi6r5KOoR2Wm', '测试用户', NULL, NULL, '[]', '', '', 0, NULL, 0, '0:0:0:0:0:0:0:1', '2023-12-30 11:42:17', '110', '2022-02-23 13:14:33', NULL, '2025-04-21 14:23:08', b'0', 121);
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 (112, 'newobject', '$2a$04$dB0z8Q819fJWz0hbaLe6B.VfHCjYgWx6LFfET5lyz3JwcqlyCkQ4C', '新对象', NULL, 100, '[]', '', '15601691235', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-03-16 23:11:38', '1', '2022-02-23 19:08:03', NULL, '2025-04-21 14:23:08', 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 (112, 'newobject', '$2a$04$dB0z8Q819fJWz0hbaLe6B.VfHCjYgWx6LFfET5lyz3JwcqlyCkQ4C', '新对象', NULL, 100, '[]', '', '15601691235', 1, NULL, 0, '127.0.0.1', '2026-06-17 09:07:20', '1', '2022-02-23 19:08:03', NULL, '2026-06-17 09:07:20', 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 (113, 'aoteman', '$2a$10$0acJOIk2D25/oC87nyclE..0lzeu9DtQ/n3geP4fkun/zIVRhHJIO', '芋道1', NULL, NULL, NULL, '', '15601691300', 0, NULL, 0, '127.0.0.1', '2022-03-19 18:38:51', '1', '2022-03-07 21:37:58', '1', '2025-05-05 15:30:53', b'0', 122);
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 (114, 'hrmgr', '$2a$10$TR4eybBioGRhBmDBWkqWLO6NIh3mzYa8KBKDDB5woiGYFVlRAi.fu', 'hr å°å§å§', NULL, NULL, '[5]', '', '15601691236', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2026-01-04 18:16:01', '1', '2022-03-19 21:50:58', NULL, '2026-01-04 18:16:01', 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 (115, 'aotemane', '$2a$04$GcyP0Vyzb2F2Yni5PuIK9ueGxM0tkZGMtDwVRwrNbtMvorzbpNsV2', '阿呆', '11222', 102, '[1,2]', '7648@qq.com', '15601691229', 2, NULL, 0, '', NULL, '1', '2022-04-30 02:55:43', '1', '2025-04-21 14:23:08', 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 (117, 'admin123', '$2a$04$sEtimsHu9YCkYY4/oqElHem2Ijc9ld20eYO6lN.g/21NfLUTDLB9W', '测试号02', '1111', 100, '[2]', '', '15601691234', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-10-02 10:16:20', '1', '2022-07-09 17:40:26', '1', '2025-05-14 09:56:04', 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 (118, 'goudan', '$2a$04$3suGZjnA6rM5bErf38u1felbgqbsPHGdRG3l9NkxPCEt2ah9Y6aJi', '狗蛋', NULL, 103, '[1]', '', '15601691239', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2025-11-23 15:28:25', '1', '2022-07-09 17:44:43', NULL, '2025-11-23 15:28:25', 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 (139, 'wwbwwb', '$2a$04$FJLIyg8lbPytP29pbZaiU.LesJvCsYfEaHqQfB0pGQhK3e9BeZmLy', '小秃头', '123', 108, '[2,4]', '', '', 1, NULL, 0, '0:0:0:0:0:0:0:1', '2024-09-10 21:03:58', NULL, '2024-09-10 21:03:58', '1', '2025-12-15 22:38:15', 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 (141, 'admin1', '$2a$04$oj6F6d7HrZ70kYVD3TNzEu.m3TPUzajOVuC66zdKna8KRerK1FmVa', '新用户', NULL, NULL, NULL, '', '', 0, '', 0, '0:0:0:0:0:0:0:1', '2025-04-08 13:09:07', '1', '2025-04-08 13:09:07', '1', '2025-05-14 19:11:48', 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 (142, 'test01', '$2a$04$4bCYWZkjxxOC4QE0LY2M9uEEKWeJbLfs489NFtQoyidL5I0FndRaO', 'test01', '', NULL, '[]', '', '19021719925', 1, '', 0, '0:0:0:0:0:0:0:1', '2025-07-29 19:47:17', '1', '2025-07-09 21:07:10', NULL, '2025-12-02 13:23:11', 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 (141, 'admin1', '$2a$04$oj6F6d7HrZ70kYVD3TNzEu.m3TPUzajOVuC66zdKna8KRerK1FmVa', '新用户', NULL, NULL, NULL, '', '', 0, '', 0, '127.0.0.1', '2026-06-17 09:37:18', '1', '2025-04-08 13:09:07', NULL, '2026-06-17 09:37:18', 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 (142, 'test01', '$2a$04$4bCYWZkjxxOC4QE0LY2M9uEEKWeJbLfs489NFtQoyidL5I0FndRaO', 'test01', '', NULL, '[]', '', '19021719925', 1, '', 0, '0:0:0:0:0:0:0:1', '2026-06-17 10:44:58', '1', '2025-07-09 21:07:10', NULL, '2026-06-17 10:44:58', 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 (143, 'a00001', '$2a$04$GhVHFviOw/SsTmiQtifHJesDYFlHMeGK7OWh7aGCCjGGVCmbHVAwa', 'a00001', NULL, 104, NULL, '', '', 0, '', 0, '0:0:0:0:0:0:0:1', '2025-12-01 16:10:13', NULL, '2025-12-01 16:10:13', '1', '2025-12-05 21:34:05', 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 (144, 'aoteman001', '$2a$04$omQOmhz8OyUFBKw77nr8KOtMp6xdvoQ1gWStjk9r8.OYT3Bv6oEYe', 'aoteman001', NULL, 116, NULL, '', '', 0, '', 1, '0:0:0:0:0:0:0:1', '2025-12-01 17:05:27', '1', '2025-12-01 17:05:27', '1', '2025-12-15 15:55:54', 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 (144, 'aoteman001', '$2a$04$omQOmhz8OyUFBKw77nr8KOtMp6xdvoQ1gWStjk9r8.OYT3Bv6oEYe', 'aoteman00112', NULL, 104, NULL, '', '', 0, '', 1, '0:0:0:0:0:0:0:1', '2025-12-01 17:05:27', '1', '2025-12-01 17:05:27', '1', '2026-06-15 18:58:56', b'0', 1);
COMMIT;
-- ----------------------------
@@ -5075,7 +5156,7 @@
INSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (17, 2, '语文', 66, '1', '2023-11-16 23:21:49', '1', '2024-09-17 18:55:31', b'0', 1);
INSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (18, 2, '数学', 22, '1', '2023-11-16 23:21:49', '1', '2024-09-17 18:55:31', b'0', 1);
INSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (19, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2025-04-19 02:49:03', b'1', 1);
INSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (20, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2025-04-19 10:49:04', b'0', 1);
INSERT INTO `yudao_demo03_course` (`id`, `student_id`, `name`, `score`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (20, 9, '滑雪', 12, '1', '2023-11-17 13:13:20', '1', '2026-06-27 08:42:44', b'0', 1);
COMMIT;
-- ----------------------------
@@ -5102,7 +5183,7 @@
BEGIN;
INSERT INTO `yudao_demo03_grade` (`id`, `student_id`, `name`, `teacher`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (7, 2, '三年 2 ç­', '周杰伦', '1', '2023-11-16 23:21:49', '1', '2024-09-17 18:55:31', b'0', 1);
INSERT INTO `yudao_demo03_grade` (`id`, `student_id`, `name`, `teacher`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (8, 5, '华为', '遥遥领先', '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', b'0', 1);
INSERT INTO `yudao_demo03_grade` (`id`, `student_id`, `name`, `teacher`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, 9, '小图', '小娃111', '1', '2023-11-17 13:10:23', '1', '2025-04-19 10:49:04', b'0', 1);
INSERT INTO `yudao_demo03_grade` (`id`, `student_id`, `name`, `teacher`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, 9, '小图', '小娃111', '1', '2023-11-17 13:10:23', '1', '2026-06-27 08:42:44', b'0', 1);
COMMIT;
-- ----------------------------
@@ -5130,7 +5211,7 @@
BEGIN;
INSERT INTO `yudao_demo03_student` (`id`, `name`, `sex`, `birthday`, `description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (2, '小白', 1, '2023-11-16 00:00:00', '<p>厉害</p>', '1', '2023-11-16 23:21:49', '1', '2024-09-17 18:55:31', b'0', 1);
INSERT INTO `yudao_demo03_student` (`id`, `name`, `sex`, `birthday`, `description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (5, '大黑', 2, '2023-11-13 00:00:00', '<p>你在教我做事?</p>', '1', '2023-11-16 23:22:46', '1', '2024-09-17 18:55:29', b'0', 1);
INSERT INTO `yudao_demo03_student` (`id`, `name`, `sex`, `birthday`, `description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, '小花', 1, '2023-11-07 00:00:00', '<p>哈哈哈</p>', '1', '2023-11-17 00:04:47', '1', '2025-04-19 10:49:04', b'0', 1);
INSERT INTO `yudao_demo03_student` (`id`, `name`, `sex`, `birthday`, `description`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `tenant_id`) VALUES (9, '小花', 1, '2023-11-07 00:00:00', '<p>哈哈哈</p>', '1', '2023-11-17 00:04:47', '1', '2026-06-27 08:42:44', b'0', 1);
COMMIT;
SET FOREIGN_KEY_CHECKS = 1;
sql/opengauss/ruoyi-vue-pro.sql
@@ -1371,6 +1371,8 @@
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');
@@ -1653,9 +1655,9 @@
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 (3443, 1, '草稿', '0', 'mes_wm_product_produce_status', 0, 'info', '', '草稿状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3444, 2, '已完成', '4', 'mes_wm_product_produce_status', 0, 'success', '', '已完成状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3445, 3, '已取消', '5', 'mes_wm_product_produce_status', 0, 'danger', '', '已取消状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '0');
COMMIT;
-- @formatter:on
@@ -5943,4 +5945,3 @@
DROP SEQUENCE IF EXISTS yudao_demo03_student_seq;
CREATE SEQUENCE yudao_demo03_student_seq
    START 10;
sql/oracle/ruoyi-vue-pro.sql
@@ -1323,6 +1323,8 @@
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');
@@ -1605,9 +1607,9 @@
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 (3443, 1, '草稿', '0', 'mes_wm_product_produce_status', 0, 'info', '', '草稿状态', '1', to_date('2026-04-05 15:53:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-05 15:53:46', '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 (3444, 2, '已完成', '4', 'mes_wm_product_produce_status', 0, 'success', '', '已完成状态', '1', to_date('2026-04-05 15:53:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-05 15:53:46', '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 (3445, 3, '已取消', '5', 'mes_wm_product_produce_status', 0, 'danger', '', '已取消状态', '1', to_date('2026-04-05 15:53:46', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-05 15:53:46', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', to_date('2026-04-16 09:47:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', to_date('2026-04-16 09:47:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', to_date('2026-04-16 09:47:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-16 09:47:00', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', to_date('2026-04-16 09:47:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', to_date('2026-04-16 09:47:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', to_date('2026-04-16 09:47:00', 'SYYYY-MM-DD HH24:MI:SS'), '1', to_date('2026-04-16 09:47:00', 'SYYYY-MM-DD HH24:MI:SS'), '0');
COMMIT;
-- @formatter:on
@@ -5801,4 +5803,3 @@
CREATE SEQUENCE yudao_demo03_student_seq
    START WITH 10;
sql/postgresql/ruoyi-vue-pro.sql
@@ -1371,6 +1371,8 @@
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');
@@ -1653,9 +1655,9 @@
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 (3443, 1, '草稿', '0', 'mes_wm_product_produce_status', 0, 'info', '', '草稿状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3444, 2, '已完成', '4', 'mes_wm_product_produce_status', 0, 'success', '', '已完成状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3445, 3, '已取消', '5', 'mes_wm_product_produce_status', 0, 'danger', '', '已取消状态', '1', '2026-04-05 15:53:46', '1', '2026-04-05 15:53:46', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3446, 0, '草稿', '0', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3447, 1, '已完成', '4', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '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 (3448, 2, '已取消', '5', 'mes_pro_task_status', 0, '', '', NULL, '1', '2026-04-16 09:47:00', '1', '2026-04-16 09:47:00', '0');
COMMIT;
-- @formatter:on
@@ -5943,4 +5945,3 @@
DROP SEQUENCE IF EXISTS yudao_demo03_student_seq;
CREATE SEQUENCE yudao_demo03_student_seq
    START 10;
sql/sqlserver/ruoyi-vue-pro.sql
@@ -3367,6 +3367,10 @@
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')
@@ -3931,11 +3935,11 @@
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 (3445, 3, N'已取消', N'5', N'mes_wm_product_produce_status', 0, N'danger', N'', N'已取消状态', N'1', N'2026-04-05 15:53:46', N'1', N'2026-04-05 15:53:46', 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 (3446, 0, N'草稿', N'0', N'mes_pro_task_status', 0, N'', N'', NULL, N'1', N'2026-04-16 09:47:00', N'1', N'2026-04-16 09:47:00', N'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 (3446, 0, N'草稿', N'0', N'mes_pro_task_status', 0, N'', N'', NULL, N'1', N'2026-04-16 09:47:00', N'1', N'2026-04-16 09:47: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 (3447, 1, N'已完成', N'4', N'mes_pro_task_status', 0, N'', N'', NULL, N'1', N'2026-04-16 09:47:00', N'1', N'2026-04-16 09:47:00', N'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 (3447, 1, N'已完成', N'4', N'mes_pro_task_status', 0, N'', N'', NULL, N'1', N'2026-04-16 09:47:00', N'1', N'2026-04-16 09:47: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 (3448, 2, N'已取消', N'5', N'mes_pro_task_status', 0, N'', N'', NULL, N'1', N'2026-04-16 09:47:00', N'1', N'2026-04-16 09:47:00', N'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 (3448, 2, N'已取消', N'5', N'mes_pro_task_status', 0, N'', N'', NULL, N'1', N'2026-04-16 09:47:00', N'1', N'2026-04-16 09:47:00', N'0')
GO
SET IDENTITY_INSERT system_dict_data OFF
GO
@@ -13915,4 +13919,3 @@
COMMIT
GO
-- @formatter:on
sql/tools/README.md
@@ -57,9 +57,11 @@
```Bash
docker compose up -d dm8
# æ³¨æ„ï¼šå¯åŠ¨å®Œ dm åŽï¼Œéœ€è¦æ‰‹åŠ¨å†æ‰§è¡Œå¦‚ä¸‹å‘½ä»¤ï¼Œå› ä¸º dm ä¸æ”¯æŒåˆå§‹åŒ–脚本
docker compose exec dm8 bash -c '/opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema.sql'
docker compose exec dm8 bash -c 'printf "SET DEFINE OFF;\n" > /tmp/schema-with-define-off.sql && cat /tmp/schema.sql >> /tmp/schema-with-define-off.sql && /opt/dmdbms/bin/disql SYSDBA/SYSDBA001 \`/tmp/schema-with-define-off.sql'
exit
```
> æ³¨æ„ï¼šé¡¹ç›® DM8 è„šæœ¬ä½¿ç”¨ `varchar(n char)` ä¿æŒå’Œ MySQL `varchar(n)` ä¸€è‡´çš„字符长度语义,建议初始化 DM8 æ—¶ä½¿ç”¨ `PAGE_SIZE=16`、`UNICODE_FLAG=1`。`sql/tools/docker-compose.yaml` å·²æŒ‰è¯¥é…ç½®æä¾›ç¤ºä¾‹ã€‚使用 `disql` å¯¼å…¥æ—¶éœ€è¦å…ˆæ‰§è¡Œ `SET DEFINE OFF;`,避免数据里的 `&` è¢«å½“作变量替换。
### 1.6 KingbaseES äººå¤§é‡‘仓
@@ -90,6 +92,25 @@
docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql'
```
### 1.8 HighGo ç€šé«˜æ•°æ®åº“
① ä¸‹è½½ç€šé«˜å®˜æ–¹ Docker é•œåƒï¼Œå¹¶åŠ è½½é•œåƒæ–‡ä»¶ã€‚åŠ è½½åŽï¼Œå°†é•œåƒæ‰“æˆæœ¬åœ°æ ‡ç­¾ï¼š
```Bash
docker load -i <highgo-image>.tar
docker tag <image>:<tag> highgo:local
```
② åœ¨é¡¹ç›® `sql/tools` ç›®å½•下运行:
```Bash
docker compose up -d highgo
```
> æ³¨æ„ï¼šä¸åŒç€šé«˜é•œåƒçš„æ•°æ®ç›®å½•可能不同,如果容器无法启动,请按镜像实际 `PGDATA` ä¿®æ”¹ `docker-compose.yaml` ä¸­çš„ `highgo` æ•°æ®å·æŒ‚载目录。
③ å¯åŠ¨å®ŒæˆåŽï¼Œéœ€è¦æ‰‹åŠ¨å¯¼å…¥ Quartz å’Œé¡¹ç›® SQL。瀚高兼容 PostgreSQL,具体客户端命令以当前镜像为准,可使用 `psql` æˆ–瀚高镜像内置的兼容客户端执行 `/tmp/quartz.sql`、`/tmp/schema.sql`。
## 1.X å®¹å™¨çš„销毁重建
开发测试过程中,有时候需要创建全新干净的数据库。由于测试数据 Docker å®¹å™¨é‡‡ç”¨æ•°æ®å· Volume æŒ‚载数据库实例的数据目录,因此销毁数据需要停止容器后,删除数据卷,然后再重新创建容器。
@@ -103,7 +124,7 @@
## 2. MySQL è½¬æ¢å…¶å®ƒæ•°æ®åº“
项目提供了 `sql/tools/convertor.py` è„šæœ¬ï¼Œæ”¯æŒå°† MySQL è½¬æ¢ä¸º Oracle、PostgreSQL、SQL Server、达梦、人大金仓、OpenGauss ç­‰æ•°æ®åº“的脚本。
项目提供了 `sql/tools/convertor.py` è„šæœ¬ï¼Œæ”¯æŒå°† MySQL è½¬æ¢ä¸º Oracle、PostgreSQL、SQL Server、达梦、人大金仓、OpenGauss、瀚高等数据库的脚本。
### 2.1 å®žçŽ°åŽŸç†
@@ -118,11 +139,12 @@
# pip3 install simple-ddl-parser
```
② åœ¨ `sql/tools/` ç›®å½•下,执行如下命令打印生成 postgres çš„脚本内容,其他可选参数有:`oracle`、`sqlserver`、`dm8`、`kingbase`、`opengauss`:
② åœ¨ `sql/tools/` ç›®å½•下,执行如下命令打印生成 postgres çš„脚本内容,其他可选参数有:`oracle`、`sqlserver`、`dm8`、`kingbase`、`opengauss`、`highgo`:
```Bash
python3 convertor.py postgres
# python3 convertor.py postgres > tmp.sql
# python3 convertor.py highgo ../mysql/ruoyi-vue-pro.sql > ../highgo/ruoyi-vue-pro.sql
```
程序将 SQL è„šæœ¬æ‰“å°åˆ°ç»ˆç«¯ï¼Œå¯ä»¥é‡å®šå‘åˆ°ä¸´æ—¶æ–‡ä»¶ `tmp.sql`。
sql/tools/convertor.py
@@ -10,6 +10,7 @@
uv run --with simple-ddl-parser convertor.py sqlserver ../mysql/ruoyi-vue-pro.sql > ../sqlserver/ruoyi-vue-pro.sql
uv run --with simple-ddl-parser convertor.py kingbase ../mysql/ruoyi-vue-pro.sql > ../kingbase/ruoyi-vue-pro.sql
uv run --with simple-ddl-parser convertor.py opengauss ../mysql/ruoyi-vue-pro.sql > ../opengauss/ruoyi-vue-pro.sql
uv run --with simple-ddl-parser convertor.py highgo ../mysql/ruoyi-vue-pro.sql > ../highgo/ruoyi-vue-pro.sql
uv run --with simple-ddl-parser convertor.py oracle ../mysql/ruoyi-vue-pro.sql > ../oracle/ruoyi-vue-pro.sql
uv run --with simple-ddl-parser convertor.py dm8 ../mysql/ruoyi-vue-pro.sql > ../dm/ruoyi-vue-pro-dm8.sql
"""
@@ -77,6 +78,9 @@
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 +183,31 @@
        """
        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,17 +233,54 @@
            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:
@@ -223,7 +289,9 @@
            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 +449,7 @@
                )
            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 +474,7 @@
        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 +503,7 @@
        """生成 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 +551,8 @@
class OracleConvertor(Convertor):
    reserved_column_names = {"level", "size"}
    def __init__(self, src):
        super().__init__(src, "Oracle")
@@ -526,10 +597,8 @@
            # 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 +623,7 @@
        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 +655,7 @@
        """拷贝 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}')",
@@ -801,7 +871,9 @@
        type = type.lower()
        if type == "varchar":
            return f"varchar({size})"
            # MySQL varchar(n) is character-oriented. DM8 may treat varchar(n)
            # as bytes, so use explicit CHAR semantics for generated scripts.
            return f"varchar({size} char)"
        if type in ("int", "int unsigned"):
            return "int"
        if type in ("bigint", "bigint unsigned"):
@@ -907,6 +979,8 @@
class KingbaseConvertor(PostgreSQLConvertor):
    reserved_column_names = {"level"}
    def __init__(self, src):
        super().__init__(src)
        self.db_type = "Kingbase"
@@ -925,7 +999,7 @@
            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,9 +1019,17 @@
class OpengaussConvertor(KingbaseConvertor):
    reserved_column_names = set()
    def __init__(self, src):
        super().__init__(src)
        self.db_type = "OpenGauss"
class HighGoConvertor(PostgreSQLConvertor):
    def __init__(self, src):
        super().__init__(src)
        self.db_type = "HighGo"
def main():
@@ -956,12 +1038,13 @@
        "type",
        type=str,
        help="目标数据库类型",
        choices=["postgres", "oracle", "sqlserver", "dm8", "kingbase", "opengauss"],
        choices=["postgres", "oracle", "sqlserver", "dm8", "kingbase", "opengauss", "highgo"],
    )
    parser.add_argument(
        "path",
        type=str,
        help="源数据库脚本路径",
        nargs="?",
        default="../mysql/ruoyi-vue-pro.sql"
    )
    args = parser.parse_args()
@@ -980,6 +1063,8 @@
        convertor = KingbaseConvertor(sql_file)
    elif args.type == "opengauss":
        convertor = OpengaussConvertor(sql_file)
    elif args.type == "highgo":
        convertor = HighGoConvertor(sql_file)
    else:
        raise NotImplementedError(f"不支持目标数据库类型: {args.type}")
sql/tools/docker-compose.yaml
@@ -7,6 +7,7 @@
    dm8: { }
    kingbase: { }
    opengauss: { }
    highgo: { }
services:
    mysql:
@@ -131,4 +132,16 @@
        volumes:
            - opengauss:/var/lib/opengauss
            - ../opengauss/ruoyi-vue-pro.sql:/tmp/schema.sql:ro
            # docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql'
            # docker compose exec opengauss bash -c '/usr/local/opengauss/bin/gsql -U $GS_USERNAME -W $GS_PASSWORD -d postgres -f /tmp/schema.sql'
    highgo:
        # ä½¿ç”¨ç€šé«˜å®˜æ–¹æä¾›çš„ Docker é•œåƒï¼ŒåŠ è½½åŽæ‰“æˆæœ¬åœ°æ ‡ç­¾ï¼š
        # docker tag <image>:<tag> highgo:local
        image: highgo:local
        restart: unless-stopped
        ports:
            - "5866:5866"
        volumes:
            - highgo:/home/highgo/hgdb/data
            - ../highgo/quartz.sql:/tmp/quartz.sql:ro
            - ../highgo/ruoyi-vue-pro.sql:/tmp/schema.sql:ro
yudao-dependencies/pom.xml
@@ -14,10 +14,10 @@
    <url>https://github.com/YunaiV/ruoyi-vue-pro</url>
    <properties>
        <revision>2026.04-SNAPSHOT</revision>
        <revision>2026.06-jdk25-SNAPSHOT</revision>
        <flatten-maven-plugin.version>1.7.2</flatten-maven-plugin.version>
        <!-- ç»Ÿä¸€ä¾èµ–管理 -->
        <spring.boot.version>4.0.6</spring.boot.version>
        <spring.boot.version>4.1.0</spring.boot.version>
        <!-- Web ç›¸å…³ -->
        <springdoc.version>3.0.3</springdoc.version>
        <knife4j.version>4.5.0</knife4j.version>
@@ -28,13 +28,13 @@
        <mybatis-plus-join.version>1.5.7</mybatis-plus-join.version>
        <dynamic-datasource.version>4.5.0</dynamic-datasource.version>
        <easy-trans.version>3.1.5</easy-trans.version>
        <redisson.version>4.3.1</redisson.version>
        <redisson.version>4.6.1</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>
        <taos.version>3.8.3</taos.version>
        <taos.version>3.8.4</taos.version>
        <!-- æ¶ˆæ¯é˜Ÿåˆ— -->
        <rocketmq-spring.version>2.3.5</rocketmq-spring.version>
        <rocketmq-spring.version>2.3.6</rocketmq-spring.version>
        <!-- æœåŠ¡ä¿éšœç›¸å…³ -->
        <lock4j.version>2.2.7</lock4j.version>
        <!-- ç›‘控相关 -->
@@ -43,42 +43,46 @@
        <opentracing.version>0.33.0</opentracing.version>
        <!-- Test æµ‹è¯•相关 -->
        <podam.version>8.0.2.RELEASE</podam.version>
        <jedis-mock.version>1.1.12</jedis-mock.version>
        <jedis-mock.version>1.1.15</jedis-mock.version>
        <mockito-inline.version>5.2.0</mockito-inline.version>
        <!-- Bpm å·¥ä½œæµç›¸å…³ -->
        <flowable.version>7.2.0</flowable.version>
        <flowable.version>8.0.0</flowable.version>
        <!-- å·¥å…·ç±»ç›¸å…³ -->
        <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>
        <hutool-5.version>5.8.46</hutool-5.version>
        <hutool-6.version>6.0.0-M22</hutool-6.version>
        <fastexcel.version>1.3.0</fastexcel.version>
        <velocity.version>2.4.1</velocity.version>
        <fastjson.version>1.2.83</fastjson.version>
        <fastjson2.version>2.0.62</fastjson2.version>
        <guava.version>33.6.0-jre</guava.version>
        <transmittable-thread-local.version>2.14.5</transmittable-thread-local.version>
        <commons-net.version>3.13.0</commons-net.version>
        <commons-lang3.version>3.20.0</commons-lang3.version>
        <jsch.version>2.28.2</jsch.version>
        <tika-core.version>3.3.0</tika-core.version>
        <jsch.version>2.28.3</jsch.version>
        <tika-core.version>3.3.1</tika-core.version>
        <ip2region.version>2.7.0</ip2region.version>
        <bizlog-sdk.version>3.0.6</bizlog-sdk.version>
        <netty.version>4.2.12.Final</netty.version>
        <netty.version>4.2.15.Final</netty.version>
        <mqtt.version>1.2.5</mqtt.version>
        <vertx.version>4.5.26</vertx.version>
        <okhttp.version>4.12.0</okhttp.version>
        <californium.version>3.14.0</californium.version>
        <j2mod.version>3.3.0</j2mod.version>
        <!-- ä¸‰æ–¹äº‘服务相关 -->
        <awssdk.version>2.44.0</awssdk.version>
        <awssdk.version>2.46.17</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>
        <weixin-java.version>4.8.4-20260623.211820</weixin-java.version>
        <bouncycastle.version>1.80</bouncycastle.version>
        <alipay-sdk-java.version>4.40.865.ALL</alipay-sdk-java.version>
    </properties>
    <dependencyManagement>
@@ -521,6 +525,11 @@
                <artifactId>fastjson</artifactId>
                <version>${fastjson.version}</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba.fastjson2</groupId>
                <artifactId>fastjson2</artifactId>
                <version>${fastjson2.version}</version>
            </dependency>
            <dependency>
                <groupId>com.google.guava</groupId>
@@ -567,6 +576,18 @@
                <groupId>org.jsoup</groupId>
                <artifactId>jsoup</artifactId>
                <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>
            <!-- Vert.x -->
@@ -657,6 +678,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>
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/collection/CollectionUtils.java
@@ -124,6 +124,22 @@
        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 @@
        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;
    }
}
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/date/LocalDateTimeUtils.java
@@ -3,7 +3,6 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.date.TemporalAccessorUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.enums.DateIntervalEnum;
@@ -16,6 +15,7 @@
import java.time.temporal.TemporalAdjusters;
import java.util.ArrayList;
import java.util.List;
import java.util.TimeZone;
import static cn.hutool.core.date.DatePattern.*;
@@ -32,6 +32,11 @@
    public static LocalDateTime EMPTY = buildTime(1970, 1, 1);
    public static DateTimeFormatter UTC_MS_WITH_XXX_OFFSET_FORMATTER = createFormatter(UTC_MS_WITH_XXX_OFFSET_PATTERN);
    /**
     * é»˜è®¤æ—¶åŒº
     */
    private static final ZoneId DEFAULT_ZONE_ID = TimeZone.getTimeZone(DateUtils.TIME_ZONE_DEFAULT).toZoneId();
    /**
     * è§£æžæ—¶é—´
@@ -63,6 +68,27 @@
    public static boolean afterNow(LocalDateTime date) {
        return date.isAfter(LocalDateTime.now());
    }
    /**
     * å°† Unix ç§’时间戳转换为默认时区的本地时间
     *
     * @param epochSecond Unix ç§’时间戳
     * @return æœ¬åœ°æ—¶é—´
     */
    public static LocalDateTime ofEpochSecond(long epochSecond) {
        return ofEpochSecond(epochSecond, DEFAULT_ZONE_ID);
    }
    /**
     * å°† Unix ç§’时间戳转换为指定时区的本地时间
     *
     * @param epochSecond Unix ç§’时间戳
     * @param zoneId æ—¶åŒºç¼–号
     * @return æœ¬åœ°æ—¶é—´
     */
    public static LocalDateTime ofEpochSecond(long epochSecond, ZoneId zoneId) {
        return LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), zoneId);
    }
    /**
@@ -236,6 +262,23 @@
        return LocalDateTime.now().with(TemporalAdjusters.firstDayOfYear()).with(LocalTime.MIN);
    }
    /**
     * èŽ·å–æœ€è¿‘ N å¤©çš„ 0 ç‚¹æ—¶åˆ»åºåˆ—(升序,含今天)
     * <p>
     * ä¾‹ï¼šgetLatestDays(3) è¿”回 [前天 00:00, æ˜¨å¤© 00:00, ä»Šå¤© 00:00]
     *
     * @param days å¤©æ•°ï¼ˆå«ä»Šå¤©ï¼‰
     * @return å‡åºçš„ LocalDateTime åˆ—表
     */
    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) {
@@ -376,7 +419,18 @@
     * @throws DateTimeException å¦‚果转换过程中发生时间超出范围或其他时间处理异常
     */
    public static Long toEpochSecond(LocalDateTime sourceDateTime) {
        return TemporalAccessorUtil.toInstant(sourceDateTime).getEpochSecond();
        return toEpochSecond(sourceDateTime, DEFAULT_ZONE_ID);
    }
    /**
     * å°†ç»™å®šçš„ {@link LocalDateTime} æŒ‰æŒ‡å®šæ—¶åŒºè½¬æ¢ä¸ºè‡ª Unix çºªå…ƒæ—¶é—´ï¼ˆ1970-01-01T00:00:00Z)以来的秒数。
     *
     * @param sourceDateTime éœ€è¦è½¬æ¢çš„æœ¬åœ°æ—¥æœŸæ—¶é—´ï¼Œä¸èƒ½ä¸ºç©º
     * @param zoneId æ—¶åŒºç¼–号
     * @return è‡ª 1970-01-01T00:00:00Z èµ·çš„秒数(epoch second)
     */
    public static Long toEpochSecond(LocalDateTime sourceDateTime, ZoneId zoneId) {
        return sourceDateTime.atZone(zoneId).toEpochSecond();
    }
}
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/http/HttpUtils.java
@@ -9,6 +9,7 @@
import org.springframework.util.StringUtils;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;
import org.springframework.web.util.UriUtils;
import java.net.URI;
import java.net.URLDecoder;
@@ -55,9 +56,59 @@
     * @return è§£ç åŽçš„路径
     */
    public static String decodeUrlPath(String path) {
        if (StrUtil.isEmpty(path)) {
            return path;
        }
        // å…ˆå°† + æ›¿æ¢ä¸º %2B,避免被 URLDecoder è§£ç ä¸ºç©ºæ ¼
        String encoded = path.replace("+", "%2B");
        return URLDecoder.decode(encoded, StandardCharsets.UTF_8);
    }
    /**
     * ç¼–码 URL è·¯å¾„,按路径段编码,保留 / åˆ†éš”符
     *
     * @param path URL è·¯å¾„,例如 20250602/xxx.pdf
     * @return ç¼–码后的路径
     */
    public static String encodeUrlPath(String path) {
        if (StrUtil.isEmpty(path)) {
            return path;
        }
        String[] segments = path.split(StrUtil.SLASH, -1);
        StringBuilder result = new StringBuilder(path.length());
        for (int i = 0; i < segments.length; i++) {
            if (i > 0) {
                result.append(StrUtil.SLASH);
            }
            result.append(encodeUrlPathSegment(segments[i]));
        }
        return result.toString();
    }
    /**
     * ç¼–码 URL è·¯å¾„段
     *
     * @param segment URL è·¯å¾„段
     * @return ç¼–码后的路径段
     */
    public static String encodeUrlPathSegment(String segment) {
        return UriUtils.encodePathSegment(segment, StandardCharsets.UTF_8);
    }
    public static String removeUrlPathQueryAndFragment(String path) {
        if (StrUtil.isEmpty(path)) {
            return path;
        }
        int endIndex = path.length();
        int queryIndex = path.indexOf('?');
        if (queryIndex >= 0) {
            endIndex = queryIndex;
        }
        int fragmentIndex = path.indexOf('#');
        if (fragmentIndex >= 0 && fragmentIndex < endIndex) {
            endIndex = fragmentIndex;
        }
        return path.substring(0, endIndex);
    }
    public static String replaceUrlQuery(String url, String key, String value) {
@@ -200,4 +251,14 @@
        }
    }
    /**
     * WebSocket URL åˆ‡æ¢æˆ HTTP URL:ws:// â†’ http://;wss:// â†’ https://;其它格式原样保留
     *
     * @param url åŽŸå§‹ URL
     * @return åˆ‡æ¢åè®®åŽçš„ URL
     */
    public static String wsUrlToHttp(String url) {
        return StrUtil.startWithIgnoreCase(url, "ws") ? "http" + url.substring(2) : url;
    }
}
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/JsonUtils.java
@@ -22,6 +22,7 @@
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
 * JSON å·¥å…·ç±»
@@ -36,13 +37,12 @@
    private static ObjectMapper buildObjectMapper() {
        SimpleModule simpleModule = new SimpleModule()
                // è§£å†³ LocalDateTime çš„序列化
                .addSerializer(LocalDateTime.class, TimestampLocalDateTimeSerializer.INSTANCE)
                .addDeserializer(LocalDateTime.class, TimestampLocalDateTimeDeserializer.INSTANCE);
        return JsonMapper.builder()
                .disable(SerializationFeature.FAIL_ON_EMPTY_BEANS)
                .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
                // å¼ºåˆ¶å°†æ•´æ•°ååºåˆ—化为 Long,解决 Redis ç¼“存反序列化时 Integer è½¬ Long çš„问题
                .enable(DeserializationFeature.USE_LONG_FOR_INTS)
                .changeDefaultPropertyInclusion(value -> JsonInclude.Value.construct(JsonInclude.Include.NON_NULL, JsonInclude.Include.NON_NULL))
                .addModule(simpleModule)
                .build();
@@ -177,6 +177,23 @@
    }
    /**
     * è§£æž JSON å­—符串成 Map,空字符串或解析失败返回 null
     *
     * @param text JSON å­—符串
     * @return Map å¯¹è±¡
     */
    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 (JacksonException e) {
            return null;
        }
    }
    /**
     * è§£æž JSON å­—符串成指定类型的对象,如果解析失败,则返回 null
     *
     * @param text å­—符串
@@ -238,6 +255,14 @@
        }
    }
    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);
    }
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/json/databind/NumberSerializer.java
@@ -4,10 +4,6 @@
import tools.jackson.core.JsonGenerator;
import tools.jackson.databind.SerializationContext;
import tools.jackson.databind.annotation.JacksonStdImpl;
import tools.jackson.databind.ser.std.StdScalarSerializer;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
 * Long åºåˆ—化规则
@@ -17,41 +13,24 @@
 * @author æ˜Ÿè¯­
 */
@JacksonStdImpl
public class NumberSerializer extends StdScalarSerializer<Number> {
public class NumberSerializer extends tools.jackson.databind.ser.jdk.NumberSerializer {
    private static final long MAX_SAFE_INTEGER = 9007199254740991L;
    private static final long MIN_SAFE_INTEGER = -9007199254740991L;
    public static final NumberSerializer INSTANCE = new NumberSerializer(Number.class);
    @SuppressWarnings("unchecked")
    public NumberSerializer(Class<? extends Number> rawType) {
        super((Class<Number>) rawType);
        super(rawType);
    }
    @Override
    public void serialize(Number value, JsonGenerator gen, SerializationContext serializers) throws JacksonException {
        // è¶…出范围 åºåˆ—化位字符串
        if (value.longValue() > MIN_SAFE_INTEGER && value.longValue() < MAX_SAFE_INTEGER) {
            writeNumber(value, gen);
            super.serialize(value, gen, serializers);
        } else {
            gen.writeString(value.toString());
        }
    }
    private static void writeNumber(Number value, JsonGenerator gen) throws JacksonException {
        if (value instanceof BigDecimal decimal) {
            gen.writeNumber(decimal);
        } else if (value instanceof BigInteger integer) {
            gen.writeNumber(integer);
        } else if (value instanceof Double doubleValue) {
            gen.writeNumber(doubleValue);
        } else if (value instanceof Float floatValue) {
            gen.writeNumber(floatValue);
        } else if (value instanceof Long longValue) {
            gen.writeNumber(longValue);
        } else {
            gen.writeNumber(value.intValue());
        }
    }
}
yudao-framework/yudao-common/src/main/java/cn/iocoder/yudao/framework/common/util/string/StrUtils.java
@@ -3,6 +3,7 @@
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;
@@ -79,6 +80,25 @@
    }
    /**
     * è½¬å°å†™æ‹¼éŸ³ï¼Œå­—之间以空格分隔,便于调用方按需拼接 / å–首字母 / æ‹¼éŸ³æœç´¢
     *
     * ä¾‹ï¼šã€Œè€å¼ ã€â†’ "lao zhang"、「ZhangSan」→ "zhangsan"
     * è‹±æ–‡ / æ•°å­— / ç¬¦å·åŽŸæ ·è¿”å›žï¼Œç©ºå€¼è¿”å›ž null
     *
     * æ³¨æ„ï¼šåº•层依赖 hutool-extra çš„ {@link PinyinUtil},需要业务模块自行引入拼音引擎依赖
     * ï¼ˆpinyin4j / TinyPinyin / Bopomofo4j ä»»é€‰å…¶ä¸€ï¼‰ï¼Œå¦åˆ™è¿è¡Œæ—¶ä¼šæŠ› NoClassDefFoundError
     *
     * @param str å­—符串
     * @return æ‹¼éŸ³ä¸²ï¼ˆä¿ç•™ç©ºæ ¼åˆ†éš”)
     */
    public static String toPinyin(String str) {
        if (StrUtil.isBlank(str)) {
            return null;
        }
        return PinyinUtil.getPinyin(str);
    }
    /**
     * æ‹¼æŽ¥æ–¹æ³•的参数
     *
     * ç‰¹æ®Šï¼šæŽ’除一些无法序列化的参数,如 ServletRequest、ServletResponse、MultipartFile
yudao-framework/yudao-common/src/test/java/cn/iocoder/yudao/framework/common/util/http/HttpUtilsTest.java
@@ -10,6 +10,36 @@
public class HttpUtilsTest {
    @Test
    public void testEncodeUrlPath() {
        // å‡†å¤‡å‚æ•°
        String path = "avatar/中文 100%+文件.jpg";
        // è°ƒç”¨
        String result = HttpUtils.encodeUrlPath(path);
        // æ–­è¨€
        assertEquals("avatar/%E4%B8%AD%E6%96%87%20100%25+%E6%96%87%E4%BB%B6.jpg", result);
    }
    @Test
    public void testDecodeUrlPath() {
        // å‡†å¤‡å‚数:+ æ˜¯è·¯å¾„字符,不应该按 query parameter è¯­ä¹‰è§£ç ä¸ºç©ºæ ¼
        String path = "avatar/%E4%B8%AD%E6%96%87%20100%25+%E6%96%87%E4%BB%B6.jpg";
        // è°ƒç”¨
        String result = HttpUtils.decodeUrlPath(path);
        // æ–­è¨€
        assertEquals("avatar/中文 100%+文件.jpg", result);
    }
    @Test
    public void testRemoveUrlPathQueryAndFragment() {
        assertEquals("avatar/test.jpg", HttpUtils.removeUrlPathQueryAndFragment("avatar/test.jpg?token=1#preview"));
        assertEquals("avatar/test.jpg", HttpUtils.removeUrlPathQueryAndFragment("avatar/test.jpg#preview?token=1"));
    }
    @Test
    public void testReplaceUrlQuery_replace() {
        // å‡†å¤‡å‚æ•°
        String url = "https://www.iocoder.cn/path?a=1&b=2";
yudao-framework/yudao-spring-boot-starter-biz-tenant/src/main/java/cn/iocoder/yudao/framework/tenant/config/YudaoTenantAutoConfiguration.java
@@ -30,6 +30,7 @@
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.cache.BatchStrategies;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
@@ -42,8 +43,11 @@
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPattern;
import java.util.*;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@AutoConfiguration
@ConditionalOnProperty(prefix = "yudao.tenant", value = "enable", matchIfMissing = true) // å…è®¸ä½¿ç”¨ yudao.tenant.enable=false ç¦ç”¨å¤šç§Ÿæˆ·
@@ -125,6 +129,7 @@
     *
     * @return å¿½ç•¥ç§Ÿæˆ·çš„ URL é›†åˆ
     */
    @SuppressWarnings("removal")
    private Set<String> getTenantIgnoreUrls() {
        Set<String> ignoreUrls = new HashSet<>();
        // èŽ·å¾—æŽ¥å£å¯¹åº”çš„ HandlerMethod é›†åˆ
@@ -139,35 +144,50 @@
                continue;
            }
            // æ·»åŠ åˆ°å¿½ç•¥çš„ URL ä¸­
            ignoreUrls.addAll(entry.getKey().getPatternValues());
            if (entry.getKey().getPatternsCondition() != null) {
                ignoreUrls.addAll(entry.getKey().getPatternsCondition().getPatterns());
            }
            if (entry.getKey().getPathPatternsCondition() != null) {
                ignoreUrls.addAll(
                        convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));
            }
        }
        return ignoreUrls;
    }
    // ========== MQ ==========
    @Bean
    public TenantRedisMessageInterceptor tenantRedisMessageInterceptor() {
        return new TenantRedisMessageInterceptor();
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(name = "cn.iocoder.yudao.framework.mq.redis.core.interceptor.RedisMessageInterceptor")
    public static class TenantRedisMQConfiguration {
        @Bean
        public TenantRedisMessageInterceptor tenantRedisMessageInterceptor() {
            return new TenantRedisMessageInterceptor();
        }
    }
    @Bean
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(name = "org.springframework.amqp.rabbit.core.RabbitTemplate")
    public TenantRabbitMQInitializer tenantRabbitMQInitializer() {
        return new TenantRabbitMQInitializer();
    public static class TenantRabbitMQConfiguration {
        @Bean
        public TenantRabbitMQInitializer tenantRabbitMQInitializer() {
            return new TenantRabbitMQInitializer();
        }
    }
    @Bean
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(name = "org.apache.rocketmq.spring.core.RocketMQTemplate")
    public TenantRocketMQInitializer tenantRocketMQInitializer() {
        return new TenantRocketMQInitializer();
    }
    public static class TenantRocketMQConfiguration {
    // ========== Job ==========
        @Bean
        public TenantRocketMQInitializer tenantRocketMQInitializer() {
            return new TenantRocketMQInitializer();
        }
    @Bean
    public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) {
        return new TenantJobAspect(tenantFrameworkService);
    }
    // ========== Redis ==========
@@ -183,7 +203,25 @@
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
                BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));
        // åˆ›å»º TenantRedisCacheManager å¯¹è±¡
        return new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration, tenantProperties.getIgnoreCaches());
        TenantRedisCacheManager cacheManager = new TenantRedisCacheManager(cacheWriter, redisCacheConfiguration,
                tenantProperties.getIgnoreCaches());
        // å¼€å¯äº‹åŠ¡æ„ŸçŸ¥ï¼š@Transactional æ–¹æ³•内的 @CacheEvict / @CachePut è‡ªåŠ¨å»¶è¿Ÿåˆ° afterCommit,
        //             é¿å…äº‹åŠ¡æœªæäº¤å°±æ¸…ç¼“å­˜è¢«å¹¶å‘è¯»ç©¿å†™è„å€¼ï¼›æ— äº‹åŠ¡æ—¶ç«‹å³ç”Ÿæ•ˆï¼Œè¡Œä¸ºä¸å˜
        cacheManager.setTransactionAware(true);
        return cacheManager;
    }
    // ========== Job ==========
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass(name = "cn.iocoder.yudao.framework.quartz.core.handler.JobHandler")
    public static class TenantJobConfiguration {
        @Bean
        public TenantJobAspect tenantJobAspect(TenantFrameworkService tenantFrameworkService) {
            return new TenantJobAspect(tenantFrameworkService);
        }
    }
}
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/config/YudaoRedisMQConsumerAutoConfiguration.java
@@ -12,7 +12,6 @@
import cn.iocoder.yudao.framework.redis.config.YudaoRedisAutoConfiguration;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.context.annotation.Bean;
@@ -70,7 +69,8 @@
    public RedisPendingMessageResendJob redisPendingMessageResendJob(List<AbstractRedisStreamMessageListener<?>> listeners,
                                                                     RedisMQTemplate redisTemplate,
                                                                     RedissonClient redissonClient) {
        return new RedisPendingMessageResendJob(listeners, redisTemplate, redissonClient);
        return new RedisPendingMessageResendJob(listeners, redisTemplate, redissonClient,
                RedisPendingMessageResendJob.DEFAULT_RESEND_LOCK_KEY);
    }
    /**
@@ -81,7 +81,8 @@
    public RedisStreamMessageCleanupJob redisStreamMessageCleanupJob(List<AbstractRedisStreamMessageListener<?>> listeners,
                                                                     RedisMQTemplate redisTemplate,
                                                                     RedissonClient redissonClient) {
        return new RedisStreamMessageCleanupJob(listeners, redisTemplate, redissonClient);
        return new RedisStreamMessageCleanupJob(listeners, redisTemplate, redissonClient,
                RedisStreamMessageCleanupJob.DEFAULT_CLEANUP_LOCK_KEY);
    }
    /**
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisPendingMessageResendJob.java
@@ -23,7 +23,9 @@
@AllArgsConstructor
public class RedisPendingMessageResendJob {
    private static final String LOCK_KEY = "redis:stream:pending-message-resend:lock";
    public static final String DEFAULT_RESEND_LOCK_KEY = "redis:stream:pending-message-resend:lock";
    public static final String IOT_RESEND_LOCK_KEY = "redis:stream:pending-message-resend:lock:iot";
    /**
     * æ¶ˆæ¯è¶…时时间,默认 5 åˆ†é’Ÿ
@@ -36,22 +38,26 @@
    private final List<AbstractRedisStreamMessageListener<?>> listeners;
    private final RedisMQTemplate redisTemplate;
    private final RedissonClient redissonClient;
    private final String resendLockKey;
    /**
     * ä¸€åˆ†é’Ÿæ‰§è¡Œä¸€æ¬¡,这里选择每分钟的 35 ç§’执行,是为了避免整点任务过多的问题
     */
    @Scheduled(cron = "35 * * * * ?")
    public void messageResend() {
        RLock lock = redissonClient.getLock(LOCK_KEY);
        // å°è¯•加锁
        RLock lock = redissonClient.getLock(resendLockKey);
        if (lock.tryLock()) {
            try {
                execute();
            } catch (Exception ex) {
                log.error("[messageResend][执行异常]", ex);
                log.error("[messageResend][执行异常][lockKey={}]", resendLockKey, ex);
            } finally {
                lock.unlock();
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            log.debug("[messageResend][未获取到锁,跳过本轮][lockKey={}]", resendLockKey);
        }
    }
yudao-framework/yudao-spring-boot-starter-mq/src/main/java/cn/iocoder/yudao/framework/mq/redis/core/job/RedisStreamMessageCleanupJob.java
@@ -23,7 +23,16 @@
@AllArgsConstructor
public class RedisStreamMessageCleanupJob {
    private static final String LOCK_KEY = "redis:stream:message-cleanup:lock";
    /**
     * ä¸šåŠ¡ MQ(Spring å®¹å™¨å†… AbstractRedisStreamMessageListener)清理任务使用的分布式锁
     */
    public static final String DEFAULT_CLEANUP_LOCK_KEY = "redis:stream:message-cleanup:lock";
    /**
     * IoT Redis æ€»çº¿æ¸…理任务使用的分布式锁(须与 {@link #DEFAULT_CLEANUP_LOCK_KEY} åŒºåˆ†ï¼Œå¦åˆ™ä¼šå…±æŠ¢ä¸€æŠŠé”ï¼Œ
     * åŒä¸€æ—¶åˆ»åªæœ‰ä¸€ä¾§èƒ½æ‰§è¡Œ XTRIM,另一侧 Stream å¯èƒ½æ— é™ç§¯åŽ‹ï¼‰
     */
    public static final String IOT_CLEANUP_LOCK_KEY = "redis:stream:message-cleanup:lock:iot";
    /**
     * ä¿ç•™çš„æ¶ˆæ¯æ•°é‡ï¼Œé»˜è®¤ä¿ç•™æœ€è¿‘ 10000 æ¡æ¶ˆæ¯
@@ -33,22 +42,29 @@
    private final List<AbstractRedisStreamMessageListener<?>> listeners;
    private final RedisMQTemplate redisTemplate;
    private final RedissonClient redissonClient;
    /**
     * Redisson é”é”®ï¼ˆå¤š Bean æ³¨å†Œæ¸…理任务时必须各不相同)
     */
    private final String cleanupLockKey;
    /**
     * æ¯å°æ—¶æ‰§è¡Œä¸€æ¬¡æ¸…理任务
     */
    @Scheduled(cron = "0 0 * * * ?")
    public void cleanup() {
        RLock lock = redissonClient.getLock(LOCK_KEY);
        // å°è¯•加锁
        RLock lock = redissonClient.getLock(cleanupLockKey);
        if (lock.tryLock()) {
            try {
                execute();
            } catch (Exception ex) {
                log.error("[cleanup][执行异常]", ex);
                log.error("[cleanup][执行异常][lockKey={}]", cleanupLockKey, ex);
            } finally {
                lock.unlock();
                if (lock.isHeldByCurrentThread()) {
                    lock.unlock();
                }
            }
        } else {
            log.debug("[cleanup][未获取到锁,跳过本轮][lockKey={}]", cleanupLockKey);
        }
    }
@@ -59,8 +75,8 @@
        StreamOperations<String, Object, Object> ops = redisTemplate.getRedisTemplate().opsForStream();
        listeners.forEach(listener -> {
            try {
                // ä½¿ç”¨ XTRIM å‘½ä»¤æ¸…理消息,只保留最近的 MAX_LEN æ¡æ¶ˆæ¯
                Long trimCount = ops.trim(listener.getStreamKey(), MAX_COUNT, true);
                // ä½¿ç”¨ XTRIM MAXLEN ç²¾ç¡®è£å‰ªï¼ˆapproximate=false),避免 ~ æ¨¡å¼ä¸‹é•¿æœŸæ˜Žæ˜¾é«˜äºŽä¸Šé™
                Long trimCount = ops.trim(listener.getStreamKey(), MAX_COUNT, false);
                if (trimCount != null && trimCount > 0) {
                    log.info("[execute][Stream({}) æ¸…理消息数量({})]", listener.getStreamKey(), trimCount);
                }
yudao-framework/yudao-spring-boot-starter-mybatis/pom.xml
@@ -84,12 +84,6 @@
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>dynamic-datasource-spring-boot4-starter</artifactId> <!-- å¤šæ•°æ®æº -->
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-undertow</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
@@ -105,6 +99,13 @@
            <groupId>org.dromara</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>
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/enums/DbTypeEnum.java
@@ -20,51 +20,49 @@
    /**
     * H2
     *
     * æ³¨æ„ï¼šH2 ä¸æ”¯æŒ find_in_set å‡½æ•°
     */
    H2(DbType.H2, "H2", ""),
    H2(DbType.H2, "H2", "POSITION(',' || CAST(#{value} AS VARCHAR) || ',' IN ',' || #{column} || ',') > 0"),
    /**
     * MySQL
     */
    MY_SQL(DbType.MYSQL, "MySQL", "FIND_IN_SET('#{value}', #{column}) <> 0"),
    MY_SQL(DbType.MYSQL, "MySQL", "FIND_IN_SET(#{value}, #{column}) <> 0"),
    /**
     * Oracle
     */
    ORACLE(DbType.ORACLE, "Oracle", "FIND_IN_SET('#{value}', #{column}) <> 0"),
    ORACLE(DbType.ORACLE, "Oracle", "INSTR(',' || #{column} || ',', ',' || #{value} || ',') > 0"),
    /**
     * PostgreSQL
     *
     * åŽä¸º openGauss ä½¿ç”¨ ProductName ä¸Ž PostgreSQL ç›¸åŒ
     */
    POSTGRE_SQL(DbType.POSTGRE_SQL,"PostgreSQL", "POSITION('#{value}' IN #{column}) <> 0"),
    POSTGRE_SQL(DbType.POSTGRE_SQL, "PostgreSQL", "POSITION(',' || CAST(#{value} AS VARCHAR) || ',' IN ',' || #{column} || ',') > 0"),
    /**
     * SQL Server
     */
    SQL_SERVER(DbType.SQL_SERVER, "Microsoft SQL Server", "CHARINDEX(',' + #{value} + ',', ',' + #{column} + ',') <> 0"),
    SQL_SERVER(DbType.SQL_SERVER, "Microsoft SQL Server", "CHARINDEX(',' + CAST(#{value} AS varchar(255)) + ',', ',' + #{column} + ',') > 0"),
    /**
     * SQL Server 2005
     */
    SQL_SERVER2005(DbType.SQL_SERVER2005, "Microsoft SQL Server 2005", "CHARINDEX(',' + #{value} + ',', ',' + #{column} + ',') <> 0"),
    SQL_SERVER2005(DbType.SQL_SERVER2005, "Microsoft SQL Server 2005", "CHARINDEX(',' + CAST(#{value} AS varchar(255)) + ',', ',' + #{column} + ',') > 0"),
    /**
     * è¾¾æ¢¦
     */
    DM(DbType.DM, "DM DBMS", "FIND_IN_SET('#{value}', #{column}) <> 0"),
    DM(DbType.DM, "DM DBMS", "FIND_IN_SET(#{value}, #{column}) <> 0"),
    /**
     * äººå¤§é‡‘仓
     */
    KINGBASE_ES(DbType.KINGBASE_ES, "KingbaseES", "POSITION('#{value}' IN #{column}) <> 0"),
    KINGBASE_ES(DbType.KINGBASE_ES, "KingbaseES", "POSITION(',' || CAST(#{value} AS VARCHAR) || ',' IN ',' || #{column} || ',') > 0"),
    /**
     * OceanBase
     */
    OCEAN_BASE(DbType.OCEAN_BASE, "OceanBase", "FIND_IN_SET('#{value}', #{column}) <> 0")
    OCEAN_BASE(DbType.OCEAN_BASE, "OceanBase", "FIND_IN_SET(#{value}, #{column}) <> 0")
    ;
@@ -95,7 +93,9 @@
    }
    public static String getFindInSetTemplate(DbType dbType) {
        return Optional.of(MAP_BY_MP.get(dbType).getFindInSetTemplate())
        return Optional.ofNullable(MAP_BY_MP.get(dbType))
                .map(DbTypeEnum::getFindInSetTemplate)
                .filter(StrUtil::isNotBlank)
                .orElseThrow(() -> new IllegalArgumentException("FIND_IN_SET not supported"));
    }
}
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/mapper/BaseMapperX.java
@@ -170,6 +170,17 @@
        return CollUtil.getFirst(list);
    }
    /**
     * èŽ·å–æ»¡è¶³æ¡ä»¶çš„æœ€æ–°ä¸€æ¡è®°å½•
     * <p>
     * ç›®çš„:解决并发场景下,插入多条记录后,使用 selectOne ä¼šæŠ¥é”™çš„问题
     *
     * @param queryWrapper æŸ¥è¯¢æ¡ä»¶
     * @return æœ€æ–°ä¸€æ¡ï¼›ä¸å­˜åœ¨è¿”回 null
     */
    default T selectLastOne(LambdaQueryWrapper<T> queryWrapper) {
        return CollUtil.getLast(selectList(queryWrapper));
    }
    default Long selectCount() {
        return selectCount(new QueryWrapper<>());
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/LambdaQueryWrapperX.java
@@ -25,6 +25,13 @@
        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)) {
            return (LambdaQueryWrapperX<T>) super.in(column, values);
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/MPJLambdaWrapperX.java
@@ -27,6 +27,13 @@
        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);
@@ -101,7 +108,6 @@
        }
        return this;
    }
    // ========== é‡å†™çˆ¶ç±»æ–¹æ³•,方便链式调用 ==========
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/query/QueryWrapperX.java
@@ -25,6 +25,13 @@
        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 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;
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtils.java
@@ -23,6 +23,7 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.regex.Pattern;
/**
 * MyBatis å·¥å…·ç±»
@@ -30,6 +31,12 @@
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_]+)*$");
    private static final String FIND_IN_SET_VALUE_PLACEHOLDER = "#{value}";
    private static final String FIND_IN_SET_COLUMN_PLACEHOLDER = "#{column}";
    public static <T> Page<T> buildPage(PageParam pageParam) {
        return buildPage(pageParam, null);
@@ -42,8 +49,11 @@
        // æŽ’序字段
        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,28 +67,50 @@
        if (wrapper instanceof QueryWrapper<T>) {
            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<T>) {
            // 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()));
            }
            lambdaQuery.last("ORDER BY " + orderBy);
            if (StrUtil.isNotEmpty(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());
        }
    }
    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;
    }
    /**
@@ -129,15 +161,43 @@
    /**
     * è·¨æ•°æ®åº“çš„ find_in_set å®žçް
     *
     * @param column å­—段名称
     * @param value  æŸ¥è¯¢å€¼(不带单引号)
     * @param columnName å­—段名称
     * @return sql
     */
    public static String findInSet(String column, Object value) {
    public static String findInSet(String columnName) {
        return findInSet(columnName, 0);
    }
    /**
     * è·¨æ•°æ®åº“çš„ find_in_set å®žçŽ°ï¼Œé€‚ç”¨äºŽåŒä¸€ä¸ª apply è¯­å¥ä¸­æœ‰å¤šä¸ªå‚数的场景
     *
     * @param columnName å­—段名称
     * @param paramIndex apply å‚数序号
     * @return sql
     */
    public static String findInSetWithParamIndex(String columnName, int paramIndex) {
        return findInSet(columnName, paramIndex);
    }
    private static String findInSet(String columnName, int paramIndex) {
        DbType dbType = JdbcUtils.getDbType();
        return findInSet(dbType, columnName, paramIndex);
    }
    static String findInSet(DbType dbType, String columnName, int paramIndex) {
        if (!isSafeColumnName(columnName)) {
            throw new IllegalArgumentException("Invalid column name: " + columnName);
        }
        if (paramIndex < 0) {
            throw new IllegalArgumentException("Invalid param index: " + paramIndex);
        }
        return DbTypeEnum.getFindInSetTemplate(dbType)
                .replace("#{column}", column)
                .replace("#{value}", StrUtil.toString(value));
                .replace(FIND_IN_SET_COLUMN_PLACEHOLDER, columnName)
                .replace(FIND_IN_SET_VALUE_PLACEHOLDER, "{" + paramIndex + "}");
    }
    private static boolean isSafeColumnName(String columnName) {
        return StrUtil.isNotEmpty(columnName) && SAFE_COLUMN_NAME_PATTERN.matcher(columnName).matches();
    }
    /**
yudao-framework/yudao-spring-boot-starter-mybatis/src/main/java/cn/iocoder/yudao/framework/translate/config/YudaoTranslateAutoConfiguration.java
@@ -11,7 +11,7 @@
    @Bean
    @ConditionalOnBean(TransService.class)
    @SuppressWarnings({"InstantiationOfUtilityClass", "SpringJavaInjectionPointsAutowiringInspection"})
    @SuppressWarnings("InstantiationOfUtilityClass")
    public TranslateUtils translateUtils(TransService transService) {
        TranslateUtils.init(transService);
        return new TranslateUtils();
yudao-framework/yudao-spring-boot-starter-mybatis/src/test/java/cn/iocoder/yudao/framework/mybatis/core/util/MyBatisUtilsTest.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,173 @@
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.annotation.DbType;
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.assertThrows;
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));
    }
    @Test
    public void testFindInSet() {
        assertEquals("FIND_IN_SET({0}, websites) <> 0",
                MyBatisUtils.findInSet(DbType.MYSQL, "websites", 0));
        assertEquals("POSITION(',' || CAST({0} AS VARCHAR) || ',' IN ',' || websites || ',') > 0",
                MyBatisUtils.findInSet(DbType.H2, "websites", 0));
        assertEquals("INSTR(',' || t.websites || ',', ',' || {0} || ',') > 0",
                MyBatisUtils.findInSet(DbType.ORACLE, "t.websites", 0));
        assertEquals("POSITION(',' || CAST({1} AS VARCHAR) || ',' IN ',' || websites || ',') > 0",
                MyBatisUtils.findInSet(DbType.POSTGRE_SQL, "websites", 1));
        assertEquals("CHARINDEX(',' + CAST({2} AS varchar(255)) + ',', ',' + websites + ',') > 0",
                MyBatisUtils.findInSet(DbType.SQL_SERVER, "websites", 2));
    }
    @Test
    public void testFindInSet_invalidColumnName() {
        assertThrows(IllegalArgumentException.class,
                () -> MyBatisUtils.findInSet(DbType.MYSQL, "websites;drop table system_tenant", 0));
        assertThrows(IllegalArgumentException.class,
                () -> MyBatisUtils.findInSet(DbType.MYSQL, "FIND_IN_SET(value, websites)", 0));
    }
    @Test
    public void testFindInSet_invalidParamIndex() {
        assertThrows(IllegalArgumentException.class,
                () -> MyBatisUtils.findInSet(DbType.MYSQL, "websites", -1));
    }
    @Test
    public void testFindInSet_applyBindsValue() {
        // å‡†å¤‡å‚æ•°
        QueryWrapper<Object> query = new QueryWrapper<>();
        String value = "test' OR 1 = 1";
        // è°ƒç”¨
        query.apply(MyBatisUtils.findInSet(DbType.MYSQL, "to_mails", 0), value);
        // æ–­è¨€ï¼šSQL ç‰‡æ®µé‡Œåªæœ‰ MyBatis Plus å‚数占位,用户输入不会被直接拼接进去
        assertEquals("(FIND_IN_SET(#{ew.paramNameValuePairs.MPGENVAL1}, to_mails) <> 0)",
                query.getSqlSegment());
        assertFalse(query.getSqlSegment().contains(value));
        assertEquals(value, query.getParamNameValuePairs().get("MPGENVAL1"));
    }
    @Test
    public void testFindInSet_applyBindsMultipleValues() {
        // å‡†å¤‡å‚æ•°
        QueryWrapper<Object> query = new QueryWrapper<>();
        String value1 = "1' OR 1 = 1";
        String value2 = "2' OR 1 = 1";
        // è°ƒç”¨
        query.apply(MyBatisUtils.findInSet(DbType.MYSQL, "tag_ids", 0)
                + " OR " + MyBatisUtils.findInSet(DbType.MYSQL, "tag_ids", 1), value1, value2);
        // æ–­è¨€ï¼šå¤šä¸ªå‚数都由 MyBatis Plus ç”Ÿæˆå ä½ç¬¦ï¼Œä¸æ‹¼æŽ¥ç”¨æˆ·è¾“å…¥
        assertEquals("(FIND_IN_SET(#{ew.paramNameValuePairs.MPGENVAL1}, tag_ids) <> 0"
                        + " OR FIND_IN_SET(#{ew.paramNameValuePairs.MPGENVAL2}, tag_ids) <> 0)",
                query.getSqlSegment());
        assertFalse(query.getSqlSegment().contains(value1));
        assertFalse(query.getSqlSegment().contains(value2));
        assertEquals(value1, query.getParamNameValuePairs().get("MPGENVAL1"));
        assertEquals(value2, query.getParamNameValuePairs().get("MPGENVAL2"));
    }
    private void assertOrderItem(OrderItem orderItem, String column, boolean asc) {
        assertEquals(column, orderItem.getColumn());
        assertEquals(asc, orderItem.isAsc());
    }
}
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoCacheAutoConfiguration.java
@@ -75,8 +75,12 @@
        RedisConnectionFactory connectionFactory = Objects.requireNonNull(redisTemplate.getConnectionFactory());
        RedisCacheWriter cacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(connectionFactory,
                BatchStrategies.scan(yudaoCacheProperties.getRedisScanBatchSize()));
        // åˆ›å»º TenantRedisCacheManager å¯¹è±¡
        return new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);
        // åˆ›å»º TimeoutRedisCacheManager å¯¹è±¡
        TimeoutRedisCacheManager cacheManager = new TimeoutRedisCacheManager(cacheWriter, redisCacheConfiguration);
        // å¼€å¯äº‹åŠ¡æ„ŸçŸ¥ï¼š@Transactional æ–¹æ³•内的 @CacheEvict / @CachePut è‡ªåŠ¨å»¶è¿Ÿåˆ° afterCommit,
        //             é¿å…äº‹åŠ¡æœªæäº¤å°±æ¸…ç¼“å­˜è¢«å¹¶å‘è¯»ç©¿å†™è„å€¼ï¼›æ— äº‹åŠ¡æ—¶ç«‹å³ç”Ÿæ•ˆï¼Œè¡Œä¸ºä¸å˜
        cacheManager.setTransactionAware(true);
        return cacheManager;
    }
}
yudao-framework/yudao-spring-boot-starter-redis/src/main/java/cn/iocoder/yudao/framework/redis/config/YudaoRedisAutoConfiguration.java
@@ -1,22 +1,11 @@
package cn.iocoder.yudao.framework.redis.config;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.hutool.core.util.StrUtil;
import org.redisson.spring.starter.RedissonAutoConfigurationV4;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.RedisSerializer;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.TreeSet;
import tools.jackson.core.type.TypeReference;
import tools.jackson.databind.ObjectMapper;
/**
 * Redis é…ç½®ç±»
@@ -43,103 +32,12 @@
        return template;
    }
    @SuppressWarnings("UnnecessaryLocalVariable")
    public static RedisSerializer<?> buildRedisSerializer() {
        // ä½¿ç”¨è‡ªå®šä¹‰åºåˆ—化器,解决 Set/List ç­‰ Collection ååºåˆ—化类型问题
        return new TypedJsonRedisSerializer();
    }
    /**
     * è‡ªå®šä¹‰ Redis åºåˆ—化器,解决 Set/List ç­‰ Collection ååºåˆ—化类型问题
     */
    private static class TypedJsonRedisSerializer implements RedisSerializer<Object> {
        private static final String JAVA_TYPE_PREFIX = "@class:";
        private static final ObjectMapper OBJECT_MAPPER = JsonUtils.getObjectMapper();
        private static final TypeReference<HashSet<Object>> HASH_SET_TYPE = new TypeReference<>() {};
        private static final TypeReference<LinkedHashSet<Object>> LINKED_HASH_SET_TYPE = new TypeReference<>() {};
        private static final TypeReference<TreeSet<Object>> TREE_SET_TYPE = new TypeReference<>() {};
        @Override
        public byte[] serialize(Object value) {
            if (value == null) {
                return new byte[0];
            }
            // å°†ç±»åž‹ä¿¡æ¯ä¸Ž JSON ä¸€èµ·å­˜å‚¨
            String json = JsonUtils.toJsonString(value);
            String typePrefix = JAVA_TYPE_PREFIX + value.getClass().getName() + ":";
            return (typePrefix + json).getBytes(StandardCharsets.UTF_8);
        }
        @Override
        public Object deserialize(byte[] bytes) {
            if (bytes == null || bytes.length == 0) {
                return null;
            }
            String content = new String(bytes, StandardCharsets.UTF_8);
            if (!content.startsWith(JAVA_TYPE_PREFIX)) {
                // å…¼å®¹æ—§æ•°æ®ï¼šæ²¡æœ‰ç±»åž‹ä¿¡æ¯çš„æƒ…况
                // æ£€æµ‹æ˜¯å¦ä¸º JSON æ•°ç»„,若是则转换为 HashSet
                return deserializeLegacyData(content);
            }
            // æå–类型信息和 JSON
            int colonIndex = content.indexOf(':', JAVA_TYPE_PREFIX.length());
            if (colonIndex == -1) {
                return deserializeLegacyData(content);
            }
            String className = content.substring(JAVA_TYPE_PREFIX.length(), colonIndex);
            String json = content.substring(colonIndex + 1);
            try {
                Class<?> clazz = Class.forName(className);
                // ç‰¹æ®Šå¤„理 Set ç±»åž‹ï¼Œå› ä¸º Jackson é»˜è®¤ä¼šå°† JSON æ•°ç»„反序列化为 List
                return deserializeWithTypeHint(json, clazz);
            } catch (ClassNotFoundException e) {
                // ç±»åž‹ä¸å­˜åœ¨æ—¶ï¼Œå°è¯•兼容解析
                return deserializeLegacyData(json);
            } catch (Exception e) {
                // è§£æžå¤±è´¥æ—¶ï¼Œå°è¯•兼容解析
                return deserializeLegacyData(json);
            }
        }
        /**
         * å…¼å®¹æ—§æ•°æ®ï¼ˆæ²¡æœ‰ç±»åž‹å‰ç¼€ï¼‰çš„解析
         * å¯¹äºŽ JSON æ•°ç»„,默认转换为 HashSet
         */
        private Object deserializeLegacyData(String content) {
            if (StrUtil.isEmpty(content)) {
                return null;
            }
            content = content.trim();
            // åˆ¤æ–­æ˜¯å¦ä¸º JSON æ•°ç»„
            if (content.startsWith("[") && content.endsWith("]")) {
                try {
                    // æ•°ç»„类型默认转为 HashSet
                    return OBJECT_MAPPER.readValue(content, HASH_SET_TYPE);
                } catch (Exception e) {
                    return JsonUtils.parseObject(content, Object.class);
                }
            }
            // å…¶ä»–类型直接解析
            return JsonUtils.parseObject(content, Object.class);
        }
        private Object deserializeWithTypeHint(String json, Class<?> clazz) {
            // å¤„理 Set æŽ¥å£çš„实现类
            if (HashSet.class.isAssignableFrom(clazz) ||
                "java.util.Collections$UnmodifiableSet".equals(clazz.getName()) ||
                "java.util.Collections$SingletonSet".equals(clazz.getName()) ||
                Set.class.isAssignableFrom(clazz)) {
                return OBJECT_MAPPER.readValue(json, HASH_SET_TYPE);
            }
            if (LinkedHashSet.class.isAssignableFrom(clazz)) {
                return OBJECT_MAPPER.readValue(json, LINKED_HASH_SET_TYPE);
            }
            if (TreeSet.class.isAssignableFrom(clazz)) {
                return OBJECT_MAPPER.readValue(json, TREE_SET_TYPE);
            }
            // ä½¿ç”¨ Jackson çš„ readValue æŒ‡å®šç±»åž‹ï¼Œç¡®ä¿ POJO æ­£ç¡®ååºåˆ—化
            return OBJECT_MAPPER.readValue(json, clazz);
        }
        RedisSerializer<Object> json = RedisSerializer.json();
        // ç‰¹æ®Šï¼šspring boot 4.x æ— éœ€è§£å†³ LocalDateTime çš„序列化
        // åŽŸå› ï¼šSpring Data Redis 4 ä½¿ç”¨ Jackson 3,RedisSerializer.json() å·²æ”¯æŒ Java Time ç±»åž‹
        return json;
    }
}
yudao-framework/yudao-spring-boot-starter-security/src/main/java/cn/iocoder/yudao/framework/security/config/YudaoWebSecurityConfigurerAdapter.java
@@ -29,11 +29,14 @@
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import org.springframework.web.util.pattern.PathPattern;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
/**
 * è‡ªå®šä¹‰çš„ Spring Security é…ç½®é€‚配器实现
@@ -153,6 +156,7 @@
        return webProperties.getAppApi().getPrefix() + url;
    }
    @SuppressWarnings("removal")
    private Multimap<HttpMethod, String> getPermitAllUrlsFromAnnotations() {
        Multimap<HttpMethod, String> result = HashMultimap.create();
        // èŽ·å¾—æŽ¥å£å¯¹åº”çš„ HandlerMethod é›†åˆ
@@ -167,7 +171,12 @@
                continue;
            }
            Set<String> urls = new HashSet<>();
            urls.addAll(entry.getKey().getPatternValues());
            if (entry.getKey().getPatternsCondition() != null) {
                urls.addAll(entry.getKey().getPatternsCondition().getPatterns());
            }
            if (entry.getKey().getPathPatternsCondition() != null) {
                urls.addAll(convertList(entry.getKey().getPathPatternsCondition().getPatterns(), PathPattern::getPatternString));
            }
            if (urls.isEmpty()) {
                continue;
            }
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/handler/JsonWebSocketMessageHandler.java
@@ -76,7 +76,7 @@
            Long tenantId = WebSocketFrameworkUtils.getTenantId(session);
            TenantUtils.execute(tenantId, () -> messageListener.onMessage(session, messageObj));
        } catch (Throwable ex) {
            log.error("[handleTextMessage][session({}) message({}) å¤„理异常]", session.getId(), message.getPayload());
            log.error("[handleTextMessage][session({}) message({}) å¤„理异常]", session.getId(), message.getPayload(), ex);
        }
    }
yudao-framework/yudao-spring-boot-starter-websocket/src/main/java/cn/iocoder/yudao/framework/websocket/core/security/LoginUserHandshakeInterceptor.java
@@ -27,9 +27,10 @@
    public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response,
                                   WebSocketHandler wsHandler, Map<String, Object> attributes) {
        LoginUser loginUser = SecurityFrameworkUtils.getLoginUser();
        if (loginUser != null) {
            WebSocketFrameworkUtils.setLoginUser(loginUser, attributes);
        if (loginUser == null) {
            return false;
        }
        WebSocketFrameworkUtils.setLoginUser(loginUser, attributes);
        return true;
    }
yudao-module-ai/pom.xml
@@ -19,10 +19,9 @@
          å›½å¤–:OpenAI、Ollama、Midjourney、StableDiffusion、Suno
    </description>
    <properties>
        <spring-ai.version>2.0.0-M7</spring-ai.version>
        <spring-ai-legacy-model.version>2.0.0-M4</spring-ai-legacy-model.version>
        <spring-ai.version>2.0.0</spring-ai.version>
        <!-- https://mvnrepository.com/artifact/com.alibaba.cloud.ai/spring-ai-alibaba -->
        <alibaba-ai.version>1.1.2.3</alibaba-ai.version>
        <alibaba-ai.version>2.0.0-M1.1</alibaba-ai.version>
        <tinyflow.version>1.2.6</tinyflow.version>
    </properties>
@@ -89,17 +88,17 @@
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-azure-openai</artifactId>
            <version>${spring-ai-legacy-model.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-anthropic</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-deepseek</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-google-genai</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
@@ -113,35 +112,10 @@
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <!-- æ™ºè°± GLM -->
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-zhipuai</artifactId>
            <version>${spring-ai-legacy-model.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-minimax</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>
        <dependency>
            <!-- é€šä¹‰åƒé—® -->
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
            <version>${alibaba-ai.version}</version>
        </dependency>
        <dependency>
            <!-- æ–‡å¿ƒä¸€è¨€ -->
            <groupId>org.springaicommunity</groupId>
            <artifactId>qianfan-core</artifactId>
            <version>1.0.0</version>
        </dependency>
        <dependency>
            <!-- æœˆä¹‹æš—面 -->
            <groupId>org.springaicommunity</groupId>
            <artifactId>moonshot-core</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!-- å‘量存储:https://db-engines.com/en/ranking/vector+dbms -->
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/ErrorCodeConstants.java
@@ -12,6 +12,7 @@
    // ========== API å¯†é’¥ 1-040-000-000 ==========
    ErrorCode API_KEY_NOT_EXISTS = new ErrorCode(1_040_000_000, "API å¯†é’¥ä¸å­˜åœ¨");
    ErrorCode API_KEY_DISABLE = new ErrorCode(1_040_000_001, "API å¯†é’¥å·²ç¦ç”¨ï¼");
    ErrorCode API_CONFIG_PLACEHOLDER_NOT_RESOLVED = new ErrorCode(1_040_000_002, "AI é…ç½®({})无法解析,请检查环境变量或配置项");
    // ========== API æ¨¡åž‹ 1-040-001-000 ==========
    ErrorCode MODEL_NOT_EXISTS = new ErrorCode(1_040_001_000, "模型不存在!");
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/enums/model/AiPlatformEnum.java
@@ -28,6 +28,7 @@
    MINI_MAX("MiniMax", "MiniMax"), // ç¨€å®‡ç§‘技
    MOONSHOT("Moonshot", "月之暗面"), // KIMI
    BAI_CHUAN("BaiChuan", "百川智能"), // ç™¾å·æ™ºèƒ½
    STEP_FUN("StepFun", "阶跃星辰"), // é˜¶è·ƒæ˜Ÿè¾°
    // ========== å›½å¤–平台 ==========
@@ -40,7 +41,7 @@
    STABLE_DIFFUSION("StableDiffusion", "StableDiffusion"), // Stability AI
    MIDJOURNEY("Midjourney", "Midjourney"), // Midjourney
    SUNO("Suno", "Suno"), // Suno AI
    GROK("Grok","Grok"), // Grok
    GROK("Grok", "Grok"), // Grok
    ;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/AiAutoConfiguration.java
@@ -6,29 +6,42 @@
import cn.iocoder.yudao.module.ai.framework.ai.core.model.AiModelFactoryImpl;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.AiWebSearchClient;
import cn.iocoder.yudao.module.ai.framework.ai.core.webserch.bocha.AiBoChaWebSearchClient;
import cn.iocoder.yudao.module.ai.tool.method.PersonService;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingModel;
import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingOptions;
import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;
import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
import io.micrometer.observation.ObservationRegistry;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import org.springframework.ai.document.MetadataMode;
import org.springframework.ai.embedding.BatchingStrategy;
import org.springframework.ai.embedding.TokenCountBatchingStrategy;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.support.ToolCallbacks;
import org.springframework.ai.tokenizer.JTokkitTokenCountEstimator;
import org.springframework.ai.tokenizer.TokenCountEstimator;
@@ -40,11 +53,11 @@
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Optional;
/**
 * èЋ道 AI è‡ªåŠ¨é…ç½®
@@ -75,30 +88,70 @@
    // ========== å„种 AI Client åˆ›å»º ==========
    @Bean
    @ConditionalOnProperty(value = "yudao.ai.gemini.enable", havingValue = "true")
    public GeminiChatModel geminiChatModel(YudaoAiProperties yudaoAiProperties) {
        YudaoAiProperties.Gemini properties = yudaoAiProperties.getGemini();
        return buildGeminiChatClient(properties);
    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
    public DashScopeChatModel dashScopeChatModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
                                                 ToolCallingManager toolCallingManager,
                                                 ObservationRegistry observationRegistry) {
        return buildTongYiChatModel(apiKey, toolCallingManager, observationRegistry);
    }
    public GeminiChatModel buildGeminiChatClient(YudaoAiProperties.Gemini properties) {
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(GeminiChatModel.MODEL_DEFAULT);
        }
        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                .openAiClient(OpenAIOkHttpClient.builder()
                        .baseUrl(GeminiChatModel.BASE_URL)
                        .apiKey(properties.getApiKey())
                        .build())
                .options(OpenAiChatOptions.builder()
                        .model(properties.getModel())
                        .temperature(properties.getTemperature())
                        .maxTokens(properties.getMaxTokens())
                        .topP(properties.getTopP())
                        .build())
                .toolCallingManager(getToolCallingManager())
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
    public DashScopeImageModel dashScopeImageModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
                                                   ObservationRegistry observationRegistry) {
        return buildTongYiImagesModel(apiKey, observationRegistry);
    }
    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnProperty(value = "spring.ai.dashscope.api-key")
    public DashScopeEmbeddingModel dashScopeEmbeddingModel(@Value("${spring.ai.dashscope.api-key}") String apiKey,
                                                           ObservationRegistry observationRegistry) {
        return buildTongYiEmbeddingModel(apiKey, null, observationRegistry);
    }
    public static DashScopeChatModel buildTongYiChatModel(String apiKey) {
        return buildTongYiChatModel(apiKey, getToolCallingManager(), getObservationRegistry());
    }
    private static DashScopeChatModel buildTongYiChatModel(String apiKey, ToolCallingManager toolCallingManager,
                                                           ObservationRegistry observationRegistry) {
        DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();
        DashScopeChatOptions options = DashScopeChatOptions.builder()
                .model(DashScopeApi.DEFAULT_CHAT_MODEL)
                .temperature(0.7)
                .build();
        return new GeminiChatModel(openAiChatModel);
        return new DashScopeChatModel(dashScopeApi, options, toolCallingManager, RetryUtils.DEFAULT_RETRY_TEMPLATE,
                observationRegistry);
    }
    public static DashScopeImageModel buildTongYiImagesModel(String apiKey) {
        return buildTongYiImagesModel(apiKey, getObservationRegistry());
    }
    private static DashScopeImageModel buildTongYiImagesModel(String apiKey, ObservationRegistry observationRegistry) {
        DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(apiKey).build();
        DashScopeImageOptions options = DashScopeImageOptions.builder()
                .model(DashScopeImageApi.DEFAULT_IMAGE_MODEL)
                .build();
        return new DashScopeImageModel(dashScopeImageApi, options, RetryUtils.DEFAULT_RETRY_TEMPLATE,
                observationRegistry);
    }
    public static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) {
        return buildTongYiEmbeddingModel(apiKey, model, getObservationRegistry());
    }
    private static DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model,
                                                                     ObservationRegistry observationRegistry) {
        DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();
        DashScopeEmbeddingOptions options = DashScopeEmbeddingOptions.builder()
                .model(StrUtil.blankToDefault(model, DashScopeApi.DEFAULT_EMBEDDING_MODEL))
                .build();
        return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, options, RetryUtils.DEFAULT_RETRY_TEMPLATE,
                observationRegistry);
    }
    @Bean
@@ -112,18 +165,18 @@
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(DouBaoChatModel.MODEL_DEFAULT);
        }
        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                .openAiClient(OpenAIOkHttpClient.builder()
        DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
                .deepSeekApi(DeepSeekApi.builder()
                        .baseUrl(DouBaoChatModel.BASE_URL)
                        .completionsPath(DouBaoChatModel.COMPLETE_PATH)
                        .apiKey(properties.getApiKey())
                        .build())
                .options(OpenAiChatOptions.builder()
                .options(DeepSeekChatOptions.builder()
                        .model(properties.getModel())
                        .temperature(properties.getTemperature())
                        .maxTokens(properties.getMaxTokens())
                        .topP(properties.getTopP())
                        .build())
                .toolCallingManager(getToolCallingManager())
                .build();
        return new DouBaoChatModel(openAiChatModel);
    }
@@ -144,13 +197,12 @@
                        .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
                        .apiKey(properties.getApiKey())
                        .build())
                .defaultOptions(DeepSeekChatOptions.builder()
                .options(DeepSeekChatOptions.builder()
                        .model(properties.getModel())
                        .temperature(properties.getTemperature())
                        .maxTokens(properties.getMaxTokens())
                        .topP(properties.getTopP())
                        .build())
                .toolCallingManager(getToolCallingManager())
                .build();
        return new SiliconFlowChatModel(openAiChatModel);
    }
@@ -166,11 +218,8 @@
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(HunYuanChatModel.MODEL_DEFAULT);
        }
        // ç‰¹æ®Šï¼šç”±äºŽæ··å…ƒå¤§æ¨¡åž‹ä¸æä¾› deepseek,而是通过知识引擎,所以需要区分下 URL
        if (StrUtil.isEmpty(properties.getBaseUrl())) {
            properties.setBaseUrl(
                    StrUtil.startWithIgnoreCase(properties.getModel(), "deepseek") ? HunYuanChatModel.DEEP_SEEK_BASE_URL
                            : HunYuanChatModel.BASE_URL);
            properties.setBaseUrl(HunYuanChatModel.BASE_URL);
        }
        // åˆ›å»º DeepSeekChatModel、HunYuanChatModel å¯¹è±¡
        DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
@@ -179,13 +228,12 @@
                        .completionsPath(HunYuanChatModel.COMPLETE_PATH)
                        .apiKey(properties.getApiKey())
                        .build())
                .defaultOptions(DeepSeekChatOptions.builder()
                .options(DeepSeekChatOptions.builder()
                        .model(properties.getModel())
                        .temperature(properties.getTemperature())
                        .maxTokens(properties.getMaxTokens())
                        .topP(properties.getTopP())
                        .build())
                .toolCallingManager(getToolCallingManager())
                .build();
        return new HunYuanChatModel(openAiChatModel);
    }
@@ -201,24 +249,15 @@
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(XingHuoChatModel.MODEL_DEFAULT);
        }
        OpenAIOkHttpClient.Builder builder = OpenAIOkHttpClient.builder()
                .baseUrl(XingHuoChatModel.BASE_URL_V1)
                .apiKey(properties.getAppKey() + ":" + properties.getSecretKey());
        if ("x1".equals(properties.getModel())) {
            builder.baseUrl(XingHuoChatModel.BASE_URL_V2);
        }
        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                .openAiClient(builder.build())
                .options(OpenAiChatOptions.builder()
        return XingHuoChatModel.builder()
                .apiKey(properties.getApiKey())
                .options(DeepSeekChatOptions.builder()
                        .model(properties.getModel())
                        .temperature(properties.getTemperature())
                        .maxTokens(properties.getMaxTokens())
                        .topP(properties.getTopP())
                        .build())
                // TODO @芋艿:星火的 function call æœ‰ bug,会报 ToolResponseMessage must have an id é”™è¯¯ï¼ï¼ï¼
                .toolCallingManager(getToolCallingManager())
                .build();
        return new XingHuoChatModel(openAiChatModel);
    }
    @Bean
@@ -232,20 +271,135 @@
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(BaiChuanChatModel.MODEL_DEFAULT);
        }
        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                .openAiClient(OpenAIOkHttpClient.builder()
        DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
                .deepSeekApi(DeepSeekApi.builder()
                        .baseUrl(BaiChuanChatModel.BASE_URL)
                        .apiKey(properties.getApiKey())
                        .completionsPath(BaiChuanChatModel.COMPLETE_PATH)
                        .build())
                .options(OpenAiChatOptions.builder()
                .options(DeepSeekChatOptions.builder()
                        .model(properties.getModel())
                        .temperature(properties.getTemperature())
                        .maxTokens(properties.getMaxTokens())
                        .topP(properties.getTopP())
                        .build())
                .toolCallingManager(getToolCallingManager())
                .build();
        return new BaiChuanChatModel(openAiChatModel);
        return new BaiChuanChatModel(deepSeekChatModel);
    }
    @Bean
    @ConditionalOnProperty(value = "yudao.ai.yiyan.enable", havingValue = "true")
    public YiYanChatModel yiYanChatClient(YudaoAiProperties yudaoAiProperties) {
        YudaoAiProperties.YiYan properties = yudaoAiProperties.getYiyan();
        return buildYiYanChatClient(properties);
    }
    public YiYanChatModel buildYiYanChatClient(YudaoAiProperties.YiYan properties) {
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(YiYanChatModel.MODEL_DEFAULT);
        }
        return new YiYanChatModel(buildDeepSeekCompatibleChatModel(
                StrUtil.blankToDefault(properties.getBaseUrl(), YiYanChatModel.BASE_URL),
                null, properties.getApiKey(), properties.getModel(), properties.getTemperature(),
                properties.getMaxTokens(), properties.getTopP()));
    }
    @Bean
    @ConditionalOnProperty(value = "yudao.ai.zhipu.enable", havingValue = "true")
    public ZhiPuChatModel zhiPuChatClient(YudaoAiProperties yudaoAiProperties) {
        YudaoAiProperties.ZhiPu properties = yudaoAiProperties.getZhipu();
        return buildZhiPuChatClient(properties);
    }
    public ZhiPuChatModel buildZhiPuChatClient(YudaoAiProperties.ZhiPu properties) {
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(ZhiPuChatModel.MODEL_DEFAULT);
        }
        DeepSeekChatModel deepSeekChatModel = DeepSeekChatModel.builder()
                .deepSeekApi(DeepSeekApi.builder()
                        .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), ZhiPuChatModel.BASE_URL))
                        .apiKey(properties.getApiKey())
                        .build())
                .options(DeepSeekChatOptions.builder()
                        .model(properties.getModel())
                        .temperature(properties.getTemperature())
                        .maxTokens(properties.getMaxTokens())
                        .topP(properties.getTopP())
                        .build())
                .build();
        return new ZhiPuChatModel(deepSeekChatModel);
    }
    @Bean
    @ConditionalOnProperty(value = "yudao.ai.minimax.enable", havingValue = "true")
    public MiniMaxChatModel miniMaxChatClient(YudaoAiProperties yudaoAiProperties) {
        YudaoAiProperties.MiniMax properties = yudaoAiProperties.getMinimax();
        return buildMiniMaxChatClient(properties);
    }
    public MiniMaxChatModel buildMiniMaxChatClient(YudaoAiProperties.MiniMax properties) {
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(MiniMaxChatModel.MODEL_DEFAULT);
        }
        return new MiniMaxChatModel(buildDeepSeekCompatibleChatModel(
                StrUtil.blankToDefault(properties.getBaseUrl(), MiniMaxChatModel.BASE_URL),
                null, properties.getApiKey(), properties.getModel(), properties.getTemperature(),
                properties.getMaxTokens(), properties.getTopP()));
    }
    @Bean
    @ConditionalOnProperty(value = "yudao.ai.moonshot.enable", havingValue = "true")
    public MoonshotChatModel moonshotChatClient(YudaoAiProperties yudaoAiProperties) {
        YudaoAiProperties.Moonshot properties = yudaoAiProperties.getMoonshot();
        return buildMoonshotChatClient(properties);
    }
    public MoonshotChatModel buildMoonshotChatClient(YudaoAiProperties.Moonshot properties) {
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(MoonshotChatModel.MODEL_DEFAULT);
        }
        return new MoonshotChatModel(buildDeepSeekCompatibleChatModel(
                StrUtil.blankToDefault(properties.getBaseUrl(), MoonshotChatModel.BASE_URL),
                MoonshotChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(),
                properties.getTemperature(), properties.getMaxTokens(), properties.getTopP()));
    }
    @Bean
    @ConditionalOnProperty(value = "yudao.ai.stepfun.enable", havingValue = "true")
    public StepFunChatModel stepFunChatClient(YudaoAiProperties yudaoAiProperties) {
        YudaoAiProperties.StepFun properties = yudaoAiProperties.getStepfun();
        return buildStepFunChatClient(properties);
    }
    public StepFunChatModel buildStepFunChatClient(YudaoAiProperties.StepFun properties) {
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(StepFunChatModel.MODEL_DEFAULT);
        }
        return new StepFunChatModel(buildDeepSeekCompatibleChatModel(
                StrUtil.blankToDefault(properties.getBaseUrl(), StepFunChatModel.BASE_URL),
                StepFunChatModel.COMPLETE_PATH, properties.getApiKey(), properties.getModel(),
                properties.getTemperature(), properties.getMaxTokens(), properties.getTopP()));
    }
    private static DeepSeekChatModel buildDeepSeekCompatibleChatModel(String baseUrl, String completionsPath,
                                                                     String apiKey, String model,
                                                                     Double temperature, Integer maxTokens,
                                                                     Double topP) {
        DeepSeekApi.Builder apiBuilder = DeepSeekApi.builder()
                .baseUrl(baseUrl)
                .apiKey(apiKey);
        if (StrUtil.isNotEmpty(completionsPath)) {
            apiBuilder.completionsPath(completionsPath);
        }
        return DeepSeekChatModel.builder()
                .deepSeekApi(apiBuilder.build())
                .options(DeepSeekChatOptions.builder()
                        .model(model)
                        .temperature(temperature)
                        .maxTokens(maxTokens)
                        .topP(topP)
                        .build())
                .build();
    }
    @Bean
@@ -261,25 +415,28 @@
        return new SunoApi(yudaoAiProperties.getSuno().getBaseUrl());
    }
    public ChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {
    @Bean
    @ConditionalOnProperty(value = "yudao.ai.grok.enable", havingValue = "true")
    public GrokChatModel grokChatClient(YudaoAiProperties yudaoAiProperties) {
        YudaoAiProperties.Grok properties = yudaoAiProperties.getGrok();
        return buildGrokChatClient(properties);
    }
    public GrokChatModel buildGrokChatClient(YudaoAiProperties.Grok properties) {
        if (StrUtil.isEmpty(properties.getModel())) {
            properties.setModel(GrokChatModel.MODEL_DEFAULT);
        }
        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
                .openAiClient(OpenAIOkHttpClient.builder()
                        .baseUrl(Optional.ofNullable(properties.getBaseUrl())
                                .orElse(GrokChatModel.BASE_URL))
                        .apiKey(properties.getApiKey())
                        .build())
                .options(OpenAiChatOptions.builder()
                        .baseUrl(StrUtil.blankToDefault(properties.getBaseUrl(), GrokChatModel.BASE_URL))
                        .apiKey(properties.getApiKey())
                        .model(properties.getModel())
                        .temperature(properties.getTemperature())
                        .maxTokens(properties.getMaxTokens())
                        .topP(properties.getTopP())
                        .build())
                .toolCallingManager(getToolCallingManager())
                .build();
        return new DouBaoChatModel(openAiChatModel);
        return new GrokChatModel(openAiChatModel);
    }
    // ========== RAG ç›¸å…³ ==========
@@ -298,6 +455,10 @@
        return SpringUtil.getBean(ToolCallingManager.class);
    }
    private static ObservationRegistry getObservationRegistry() {
        return SpringUtil.getBean(ObservationRegistry.class);
    }
    // ========== Web Search ç›¸å…³ ==========
    @Bean
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/config/YudaoAiProperties.java
@@ -14,11 +14,6 @@
public class YudaoAiProperties {
    /**
     * è°·æ­Œ Gemini
     */
    private Gemini gemini;
    /**
     * å­—节豆包
     */
    private DouBao doubao;
@@ -44,6 +39,36 @@
    private BaiChuan baichuan;
    /**
     * æ–‡å¿ƒä¸€è¨€
     */
    private YiYan yiyan;
    /**
     * æ™ºè°±
     */
    private ZhiPu zhipu;
    /**
     * MiniMax
     */
    private MiniMax minimax;
    /**
     * æœˆä¹‹æš—面
     */
    private Moonshot moonshot;
    /**
     * é˜¶è·ƒæ˜Ÿè¾°
     */
    private StepFun stepfun;
    /**
     * Grok
     */
    private Grok grok;
    /**
     * Midjourney ç»˜å›¾
     */
    private Midjourney midjourney;
@@ -58,19 +83,6 @@
     * ç½‘络搜索
     */
    private WebSearch webSearch;
    @Data
    public static class Gemini {
        private String enable;
        private String apiKey;
        private String model;
        private Double temperature;
        private Integer maxTokens;
        private Double topP;
    }
    @Data
    public static class DouBao {
@@ -116,9 +128,7 @@
    public static class XingHuo {
        private String enable;
        private String appId;
        private String appKey;
        private String secretKey;
        private String apiKey;
        private String model;
        private Double temperature;
@@ -131,6 +141,62 @@
    public static class BaiChuan {
        private String enable;
        private String apiKey;
        private String model;
        private Double temperature;
        private Integer maxTokens;
        private Double topP;
    }
    @Data
    public static class YiYan {
        private String enable;
        private String baseUrl;
        private String apiKey;
        private String model;
        private Double temperature;
        private Integer maxTokens;
        private Double topP;
    }
    @Data
    public static class ZhiPu {
        private String enable;
        private String baseUrl;
        private String apiKey;
        private String model;
        private Double temperature;
        private Integer maxTokens;
        private Double topP;
    }
    @Data
    public static class MiniMax {
        private String enable;
        private String baseUrl;
        private String apiKey;
        private String model;
        private Double temperature;
        private Integer maxTokens;
        private Double topP;
    }
    @Data
    public static class Moonshot {
        private String enable;
        private String baseUrl;
        private String apiKey;
        private String model;
@@ -175,6 +241,20 @@
    }
    @Data
    public static class StepFun {
        private String enable;
        private String apiKey;
        private String baseUrl;
        private String model;
        private Double temperature;
        private Integer maxTokens;
        private Double topP;
    }
    @Data
    public static class WebSearch {
        private boolean enable;
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/AiModelFactoryImpl.java
@@ -8,52 +8,40 @@
import cn.hutool.core.util.RuntimeUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.util.spring.SpringUtils;
import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
import cn.iocoder.yudao.module.ai.framework.ai.config.AiAutoConfiguration;
import cn.iocoder.yudao.module.ai.framework.ai.config.YudaoAiProperties;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.midjourney.api.MidjourneyApi;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageApi;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.suno.api.SunoApi;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
import cn.iocoder.yudao.module.ai.util.AiUtils;
import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeChatAutoConfiguration;
import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeEmbeddingAutoConfiguration;
import com.alibaba.cloud.ai.autoconfigure.dashscope.DashScopeImageAutoConfiguration;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingModel;
import com.alibaba.cloud.ai.dashscope.embedding.text.DashScopeEmbeddingOptions;
import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.core.credential.KeyCredential;
import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.google.genai.Client;
import com.google.genai.types.HttpOptions;
import io.micrometer.observation.ObservationRegistry;
import io.milvus.client.MilvusServiceClient;
import io.qdrant.client.QdrantClient;
import io.qdrant.client.QdrantGrpcClient;
import lombok.SneakyThrows;
import org.springaicommunity.moonshot.MoonshotChatModel;
import org.springaicommunity.moonshot.MoonshotChatOptions;
import org.springaicommunity.moonshot.api.MoonshotApi;
import org.springaicommunity.qianfan.QianFanChatModel;
import org.springaicommunity.qianfan.QianFanEmbeddingModel;
import org.springaicommunity.qianfan.QianFanEmbeddingOptions;
import org.springaicommunity.qianfan.QianFanImageModel;
import org.springaicommunity.qianfan.api.QianFanApi;
import org.springaicommunity.qianfan.api.QianFanImageApi;
import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
import org.springframework.ai.azure.openai.AzureOpenAiEmbeddingModel;
import org.springframework.ai.anthropic.AnthropicChatModel;
import org.springframework.ai.anthropic.AnthropicChatOptions;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
@@ -62,37 +50,24 @@
import org.springframework.ai.embedding.BatchingStrategy;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.observation.EmbeddingModelObservationConvention;
import org.springframework.ai.google.genai.GoogleGenAiChatModel;
import org.springframework.ai.google.genai.GoogleGenAiChatOptions;
import org.springframework.ai.image.ImageModel;
import org.springframework.ai.minimax.MiniMaxChatModel;
import org.springframework.ai.minimax.MiniMaxChatOptions;
import org.springframework.ai.minimax.MiniMaxEmbeddingModel;
import org.springframework.ai.minimax.MiniMaxEmbeddingOptions;
import org.springframework.ai.minimax.api.MiniMaxApi;
import org.springframework.ai.model.anthropic.autoconfigure.AnthropicChatAutoConfiguration;
import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatAutoConfiguration;
import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingAutoConfiguration;
import org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiEmbeddingProperties;
import org.springframework.ai.model.deepseek.autoconfigure.DeepSeekChatAutoConfiguration;
import org.springframework.ai.model.minimax.autoconfigure.MiniMaxChatAutoConfiguration;
import org.springframework.ai.model.minimax.autoconfigure.MiniMaxEmbeddingAutoConfiguration;
import org.springframework.ai.model.google.genai.autoconfigure.chat.GoogleGenAiChatAutoConfiguration;
import org.springframework.ai.model.ollama.autoconfigure.OllamaChatAutoConfiguration;
import org.springframework.ai.model.openai.autoconfigure.OpenAiChatAutoConfiguration;
import org.springframework.ai.model.openai.autoconfigure.OpenAiEmbeddingAutoConfiguration;
import org.springframework.ai.model.openai.autoconfigure.OpenAiImageAutoConfiguration;
import org.springframework.ai.model.stabilityai.autoconfigure.StabilityAiImageAutoConfiguration;
import org.springframework.ai.model.tool.ToolCallingManager;
import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiChatAutoConfiguration;
import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiEmbeddingAutoConfiguration;
import org.springframework.ai.model.zhipuai.autoconfigure.ZhiPuAiImageAutoConfiguration;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.ai.ollama.OllamaEmbeddingModel;
import org.springframework.ai.ollama.api.OllamaApi;
import org.springframework.ai.ollama.api.OllamaEmbeddingOptions;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiEmbeddingModel;
import org.springframework.ai.openai.OpenAiEmbeddingOptions;
import org.springframework.ai.openai.OpenAiImageModel;
import org.springframework.ai.anthropic.AnthropicChatModel;
import org.springframework.ai.openai.*;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.ai.stabilityai.StabilityAiImageModel;
import org.springframework.ai.stabilityai.api.StabilityAiApi;
import org.springframework.ai.vectorstore.SimpleVectorStore;
@@ -110,18 +85,17 @@
import org.springframework.ai.vectorstore.redis.RedisVectorStore;
import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreAutoConfiguration;
import org.springframework.ai.vectorstore.redis.autoconfigure.RedisVectorStoreProperties;
import org.springframework.ai.zhipuai.*;
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
import org.springframework.ai.zhipuai.api.ZhiPuAiImageApi;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.data.redis.autoconfigure.DataRedisProperties;
import org.springframework.web.client.RestClient;
import redis.clients.jedis.JedisPooled;
import redis.clients.jedis.DefaultJedisClientConfig;
import redis.clients.jedis.HostAndPort;
import redis.clients.jedis.JedisClientConfig;
import redis.clients.jedis.RedisClient;
import java.io.File;
import java.time.Duration;
import java.util.List;
import java.util.Collections;
import java.util.Map;
import java.util.Timer;
import java.util.TimerTask;
@@ -136,7 +110,9 @@
public class AiModelFactoryImpl implements AiModelFactory {
    @Override
    public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String apiKey, String url) {
    public ChatModel getOrCreateChatModel(AiPlatformEnum platform, String rawApiKey, String rawUrl) {
        final String apiKey = resolveSpringPlaceholders(rawApiKey);
        final String url = resolveSpringPlaceholders(rawUrl);
        String cacheKey = buildClientCacheKey(ChatModel.class, platform, apiKey, url);
        return Singleton.get(cacheKey, (Func0<ChatModel>) () -> {
            // noinspection EnhancedSwitchMigration
@@ -159,6 +135,8 @@
                    return buildMiniMaxChatModel(apiKey, url);
                case MOONSHOT:
                    return buildMoonshotChatModel(apiKey, url);
                case STEP_FUN:
                    return buildStepFunChatModel(apiKey, url);
                case XING_HUO:
                    return buildXingHuoChatModel(apiKey);
                case BAI_CHUAN:
@@ -170,11 +148,11 @@
                case ANTHROPIC:
                    return buildAnthropicChatModel(apiKey, url);
                case GEMINI:
                    return buildGeminiChatModel(apiKey);
                    return buildGeminiChatModel(apiKey, url);
                case OLLAMA:
                    return buildOllamaChatModel(url);
                case GROK:
                    return buildGrokChatModel(apiKey,url);
                    return buildGrokChatModel(apiKey, url);
                default:
                    throw new IllegalArgumentException(StrUtil.format("未知平台({})", platform));
            }
@@ -188,7 +166,7 @@
            case TONG_YI:
                return SpringUtil.getBean(DashScopeChatModel.class);
            case YI_YAN:
                return SpringUtil.getBean(QianFanChatModel.class);
                return SpringUtil.getBean(YiYanChatModel.class);
            case DEEP_SEEK:
                return SpringUtil.getBean(DeepSeekChatModel.class);
            case DOU_BAO:
@@ -198,23 +176,23 @@
            case SILICON_FLOW:
                return SpringUtil.getBean(SiliconFlowChatModel.class);
            case ZHI_PU:
                return SpringUtil.getBean(ZhiPuAiChatModel.class);
                return SpringUtil.getBean(ZhiPuChatModel.class);
            case MINI_MAX:
                return SpringUtil.getBean(MiniMaxChatModel.class);
            case MOONSHOT:
                return SpringUtil.getBean(MoonshotChatModel.class);
            case STEP_FUN:
                return SpringUtil.getBean(StepFunChatModel.class);
            case XING_HUO:
                return SpringUtil.getBean(XingHuoChatModel.class);
            case BAI_CHUAN:
                return SpringUtil.getBean(BaiChuanChatModel.class);
            case OPENAI:
                return SpringUtil.getBean(OpenAiChatModel.class);
            case AZURE_OPENAI:
                return SpringUtil.getBean(AzureOpenAiChatModel.class);
            case ANTHROPIC:
                return SpringUtil.getBean(AnthropicChatModel.class);
            case GEMINI:
                return SpringUtil.getBean(GeminiChatModel.class);
                return SpringUtil.getBean(GoogleGenAiChatModel.class);
            case OLLAMA:
                return SpringUtil.getBean(OllamaChatModel.class);
            default:
@@ -228,10 +206,6 @@
        switch (platform) {
            case TONG_YI:
                return SpringUtil.getBean(DashScopeImageModel.class);
            case YI_YAN:
                return SpringUtil.getBean(QianFanImageModel.class);
            case ZHI_PU:
                return SpringUtil.getBean(ZhiPuAiImageModel.class);
            case SILICON_FLOW:
                return SpringUtil.getBean(SiliconFlowImageModel.class);
            case OPENAI:
@@ -244,19 +218,17 @@
    }
    @Override
    public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String apiKey, String url) {
    public ImageModel getOrCreateImageModel(AiPlatformEnum platform, String rawApiKey, String rawUrl) {
        String apiKey = resolveSpringPlaceholders(rawApiKey);
        String url = resolveSpringPlaceholders(rawUrl);
        // noinspection EnhancedSwitchMigration
        switch (platform) {
            case TONG_YI:
                return buildTongYiImagesModel(apiKey);
            case YI_YAN:
                return buildQianFanImageModel(apiKey);
            case ZHI_PU:
                return buildZhiPuAiImageModel(apiKey, url);
            case OPENAI:
                return buildOpenAiImageModel(apiKey, url);
            case SILICON_FLOW:
                return buildSiliconFlowImageModel(apiKey,url);
                return buildSiliconFlowImageModel(apiKey, url);
            case STABLE_DIFFUSION:
                return buildStabilityAiImageModel(apiKey, url);
            default:
@@ -265,9 +237,11 @@
    }
    @Override
    public MidjourneyApi getOrCreateMidjourneyApi(String apiKey, String url) {
        String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(), apiKey,
                url);
    public MidjourneyApi getOrCreateMidjourneyApi(String rawApiKey, String rawUrl) {
        final String apiKey = resolveSpringPlaceholders(rawApiKey);
        final String url = resolveSpringPlaceholders(rawUrl);
        String cacheKey = buildClientCacheKey(MidjourneyApi.class, AiPlatformEnum.MIDJOURNEY.getPlatform(),
                apiKey, url);
        return Singleton.get(cacheKey, (Func0<MidjourneyApi>) () -> {
            YudaoAiProperties.Midjourney properties = SpringUtil.getBean(YudaoAiProperties.class)
                    .getMidjourney();
@@ -276,25 +250,23 @@
    }
    @Override
    public SunoApi getOrCreateSunoApi(String apiKey, String url) {
    public SunoApi getOrCreateSunoApi(String rawApiKey, String rawUrl) {
        final String apiKey = resolveSpringPlaceholders(rawApiKey);
        final String url = resolveSpringPlaceholders(rawUrl);
        String cacheKey = buildClientCacheKey(SunoApi.class, AiPlatformEnum.SUNO.getPlatform(), apiKey, url);
        return Singleton.get(cacheKey, (Func0<SunoApi>) () -> new SunoApi(url));
    }
    @Override
    @SuppressWarnings("EnhancedSwitchMigration")
    public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String apiKey, String url, String model) {
    public EmbeddingModel getOrCreateEmbeddingModel(AiPlatformEnum platform, String rawApiKey, String rawUrl, String model) {
        final String apiKey = resolveSpringPlaceholders(rawApiKey);
        final String url = resolveSpringPlaceholders(rawUrl);
        String cacheKey = buildClientCacheKey(EmbeddingModel.class, platform, apiKey, url, model);
        return Singleton.get(cacheKey, (Func0<EmbeddingModel>) () -> {
            switch (platform) {
                case TONG_YI:
                    return buildTongYiEmbeddingModel(apiKey, model);
                case YI_YAN:
                    return buildYiYanEmbeddingModel(apiKey, model);
                case ZHI_PU:
                    return buildZhiPuEmbeddingModel(apiKey, url, model);
                case MINI_MAX:
                    return buildMiniMaxEmbeddingModel(apiKey, url, model);
                case OPENAI:
                    return buildOpenAiEmbeddingModel(apiKey, url, model);
                case AZURE_OPENAI:
@@ -336,60 +308,31 @@
        return StrUtil.format("{}#{}", clazz.getName(), ArrayUtil.join(params, "_"));
    }
    private static String resolveSpringPlaceholders(String value) {
        // yml é…ç½®çš„占位符由 Spring è‡ªåŠ¨è§£æžï¼›DB é‡Œä¿å­˜çš„ ${xxx} éœ€è¦åœ¨è¿™é‡Œæ‰‹åŠ¨è§£æžã€‚
        return AiUtils.resolveSpringPlaceholders(value);
    }
    // ========== å„种创建 spring-ai å®¢æˆ·ç«¯çš„æ–¹æ³• ==========
    /**
     * å¯å‚考 {@link DashScopeChatAutoConfiguration} çš„ dashscopeChatModel æ–¹æ³•
     */
    private static DashScopeChatModel buildTongYiChatModel(String key) {
        DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(key).build();
        DashScopeChatOptions options = DashScopeChatOptions
                .builder()
                .model(DashScopeApi.DEFAULT_CHAT_MODEL)
                .temperature(0.7)
                .build();
        return DashScopeChatModel
                .builder()
                .dashScopeApi(dashScopeApi)
                .defaultOptions(options)
                .toolCallingManager(getToolCallingManager())
                .build();
        return AiAutoConfiguration.buildTongYiChatModel(key);
    }
    /**
     * å¯å‚考 {@link DashScopeImageAutoConfiguration} çš„ dashScopeImageModel æ–¹æ³•
     */
    private static DashScopeImageModel buildTongYiImagesModel(String key) {
        DashScopeImageApi dashScopeImageApi = DashScopeImageApi.builder().apiKey(key).build();
        return DashScopeImageModel.builder()
                .dashScopeApi(dashScopeImageApi)
                .build();
        return AiAutoConfiguration.buildTongYiImagesModel(key);
    }
    /**
     * å¯å‚考 QianFanChatAutoConfiguration çš„ qianFanChatModel æ–¹æ³•
     */
    private static QianFanChatModel buildYiYanChatModel(String key) {
        // TODO spring ai qianfan æœ‰ bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6
        List<String> keys = StrUtil.split(key, '|');
        Assert.equals(keys.size(), 2, "YiYanChatClient çš„密钥需要 (appKey|secretKey) æ ¼å¼");
        String appKey = keys.get(0);
        String secretKey = keys.get(1);
        QianFanApi qianFanApi = new QianFanApi(appKey, secretKey);
        return new QianFanChatModel(qianFanApi);
    }
    /**
     * å¯å‚考 QianFanEmbeddingAutoConfiguration çš„ qianFanImageModel æ–¹æ³•
     */
    private QianFanImageModel buildQianFanImageModel(String key) {
        // TODO spring ai qianfan æœ‰ bug,无法使用 https://github.com/spring-ai-community/qianfan/issues/6
        List<String> keys = StrUtil.split(key, '|');
        Assert.equals(keys.size(), 2, "YiYanChatClient çš„密钥需要 (appKey|secretKey) æ ¼å¼");
        String appKey = keys.get(0);
        String secretKey = keys.get(1);
        QianFanImageApi qianFanApi = new QianFanImageApi(appKey, secretKey);
        return new QianFanImageModel(qianFanApi);
    private ChatModel buildYiYanChatModel(String apiKey) {
        YudaoAiProperties.YiYan properties = new YudaoAiProperties.YiYan()
                .setApiKey(apiKey);
        return new AiAutoConfiguration().buildYiYanChatClient(properties);
    }
    /**
@@ -401,8 +344,7 @@
                .temperature(0.7).build();
        return DeepSeekChatModel.builder()
                .deepSeekApi(deepSeekApi)
                .defaultOptions(options)
                .toolCallingManager(getToolCallingManager())
                .options(options)
                .build();
    }
@@ -434,62 +376,47 @@
    }
    /**
     * å¯å‚考 {@link ZhiPuAiChatAutoConfiguration} çš„ zhiPuAiChatModel æ–¹æ³•
     * å¯å‚考 {@link AiAutoConfiguration#zhiPuChatClient(YudaoAiProperties)}
     */
    private ZhiPuAiChatModel buildZhiPuChatModel(String apiKey, String url) {
        ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey);
        if (StrUtil.isNotEmpty(url)) {
            zhiPuAiApiBuilder.baseUrl(url);
        }
        ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder().model(ZhiPuAiApi.DEFAULT_CHAT_MODEL).temperature(0.7).build();
        return new ZhiPuAiChatModel(zhiPuAiApiBuilder.build(), options, getToolCallingManager(), new org.springframework.core.retry.RetryTemplate(),
                getObservationRegistry().getIfAvailable());
    private ZhiPuChatModel buildZhiPuChatModel(String apiKey, String url) {
        YudaoAiProperties.ZhiPu properties = new YudaoAiProperties.ZhiPu()
                .setBaseUrl(url).setApiKey(apiKey);
        return new AiAutoConfiguration().buildZhiPuChatClient(properties);
    }
    /**
     * å¯å‚考 {@link ZhiPuAiImageAutoConfiguration} çš„ zhiPuAiImageModel æ–¹æ³•
     */
    private ZhiPuAiImageModel buildZhiPuAiImageModel(String apiKey, String url) {
        ZhiPuAiImageApi zhiPuAiApi = StrUtil.isEmpty(url) ? new ZhiPuAiImageApi(apiKey)
                : new ZhiPuAiImageApi(url, apiKey, RestClient.builder());
        return new ZhiPuAiImageModel(zhiPuAiApi);
    }
    /**
     * å¯å‚考 {@link MiniMaxChatAutoConfiguration} çš„ miniMaxChatModel æ–¹æ³•
     * å¯å‚考 {@link AiAutoConfiguration#miniMaxChatClient(YudaoAiProperties)}
     */
    private MiniMaxChatModel buildMiniMaxChatModel(String apiKey, String url) {
        MiniMaxApi miniMaxApi = StrUtil.isEmpty(url) ? new MiniMaxApi(apiKey)
                : new MiniMaxApi(url, apiKey);
        MiniMaxChatOptions options = MiniMaxChatOptions.builder().model(MiniMaxApi.DEFAULT_CHAT_MODEL).temperature(0.7).build();
        return new MiniMaxChatModel(miniMaxApi, options, getToolCallingManager(), new org.springframework.core.retry.RetryTemplate());
        YudaoAiProperties.MiniMax properties = new YudaoAiProperties.MiniMax()
                .setBaseUrl(url).setApiKey(apiKey);
        return new AiAutoConfiguration().buildMiniMaxChatClient(properties);
    }
    /**
     * å¯å‚考 MoonshotChatAutoConfiguration çš„ moonshotChatModel æ–¹æ³•
     * å¯å‚考 {@link AiAutoConfiguration#moonshotChatClient(YudaoAiProperties)}
     */
    private MoonshotChatModel buildMoonshotChatModel(String apiKey, String url) {
        MoonshotApi.Builder moonshotApiBuilder = MoonshotApi.builder()
                .apiKey(apiKey);
        if (StrUtil.isNotEmpty(url)) {
            moonshotApiBuilder.baseUrl(url);
        }
        MoonshotChatOptions options = MoonshotChatOptions.builder().model(MoonshotApi.DEFAULT_CHAT_MODEL).build();
        return MoonshotChatModel.builder()
                .moonshotApi(moonshotApiBuilder.build())
                .defaultOptions(options)
                .toolCallingManager(getToolCallingManager())
                .build();
        YudaoAiProperties.Moonshot properties = new YudaoAiProperties.Moonshot()
                .setBaseUrl(url).setApiKey(apiKey);
        return new AiAutoConfiguration().buildMoonshotChatClient(properties);
    }
    /**
     * å¯å‚考 {@link AiAutoConfiguration#stepFunChatClient(YudaoAiProperties)}
     */
    private StepFunChatModel buildStepFunChatModel(String apiKey, String url) {
        YudaoAiProperties.StepFun properties = new YudaoAiProperties.StepFun()
                .setBaseUrl(url).setApiKey(apiKey);
        return new AiAutoConfiguration().buildStepFunChatClient(properties);
    }
    /**
     * å¯å‚考 {@link AiAutoConfiguration#xingHuoChatClient(YudaoAiProperties)}
     */
    private static XingHuoChatModel buildXingHuoChatModel(String key) {
        List<String> keys = StrUtil.split(key, '|');
        Assert.equals(keys.size(), 2, "XingHuoChatClient çš„密钥需要 (appKey|secretKey) æ ¼å¼");
    private static XingHuoChatModel buildXingHuoChatModel(String apiKey) {
        YudaoAiProperties.XingHuo properties = new YudaoAiProperties.XingHuo()
                .setAppKey(keys.get(0)).setSecretKey(keys.get(1));
                .setApiKey(apiKey).setModel(XingHuoChatModel.MODEL_DEFAULT);
        return new AiAutoConfiguration().buildXingHuoChatClient(properties);
    }
@@ -507,52 +434,73 @@
     */
    private static OpenAiChatModel buildOpenAiChatModel(String openAiToken, String url) {
        return OpenAiChatModel.builder()
                .openAiClient(buildOpenAiClient(openAiToken, url))
                .toolCallingManager(getToolCallingManager())
                .options(buildOpenAiChatOptions(openAiToken, url).build())
                .build();
    }
    /**
     * å¯å‚考 {@link AzureOpenAiChatAutoConfiguration}
     */
    private static AzureOpenAiChatModel buildAzureOpenAiChatModel(String apiKey, String url) {
        // TODO @芋艿:使用前,请测试,暂时没密钥!!!
        OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder()
                .endpoint(url).credential(new KeyCredential(apiKey));
        return AzureOpenAiChatModel.builder()
                .openAIClientBuilder(openAIClientBuilder)
                .toolCallingManager(getToolCallingManager())
    private static OpenAiChatModel buildAzureOpenAiChatModel(String openAiToken, String url) {
        return OpenAiChatModel.builder()
                .options(buildOpenAiChatOptions(openAiToken, url)
                        .azure(true)
                        .build())
                .build();
    }
    private static OpenAiChatOptions.Builder buildOpenAiChatOptions(String apiKey, String url) {
        OpenAiChatOptions.Builder optionsBuilder = OpenAiChatOptions.builder().apiKey(apiKey);
        if (StrUtil.isNotEmpty(url)) {
            optionsBuilder.baseUrl(url);
        }
        return optionsBuilder;
    }
    /**
     * å¯å‚考 {@link AnthropicChatAutoConfiguration} çš„ anthropicApi æ–¹æ³•
     */
    private static AnthropicChatModel buildAnthropicChatModel(String apiKey, String url) {
        AnthropicOkHttpClient.Builder builder = AnthropicOkHttpClient.builder().apiKey(apiKey);
        AnthropicChatOptions.Builder optionsBuilder = AnthropicChatOptions.builder().apiKey(apiKey);
        if (StrUtil.isNotEmpty(url)) {
            builder.baseUrl(url);
            optionsBuilder.baseUrl(url);
        }
        return AnthropicChatModel.builder()
                .anthropicClient(builder.build())
                .toolCallingManager(getToolCallingManager())
                .options(optionsBuilder.build())
                .build();
    }
    /**
     * å¯å‚考 {@link AiAutoConfiguration#buildGeminiChatClient(YudaoAiProperties.Gemini)}
     * å¯å‚考 {@link GoogleGenAiChatAutoConfiguration} çš„ googleGenAiChatModel æ–¹æ³•
     */
    private static GeminiChatModel buildGeminiChatModel(String apiKey) {
        YudaoAiProperties.Gemini properties = SpringUtil.getBean(YudaoAiProperties.class)
                .getGemini().setApiKey(apiKey);
        return new AiAutoConfiguration().buildGeminiChatClient(properties);
    private static GoogleGenAiChatModel buildGeminiChatModel(String apiKey, String url) {
        Client.Builder clientBuilder = Client.builder().apiKey(apiKey);
        if (StrUtil.isNotBlank(url)) {
            clientBuilder.httpOptions(HttpOptions.builder()
                    .baseUrl(url)
                    // TeamOrouter çš„ Gemini åŽŸç”Ÿåè®®ä½¿ç”¨ Authorization Bearer é‰´æƒ
                    .headers(Collections.singletonMap("Authorization", "Bearer " + apiKey))
                    .build());
        }
        return GoogleGenAiChatModel.builder()
                .genAiClient(clientBuilder.build())
                .options(GoogleGenAiChatOptions.builder()
                        .model("gemini-2.5-flash")
                        .build())
                .toolCallingManager(SpringUtil.getBean(ToolCallingManager.class))
                .retryTemplate(RetryUtils.DEFAULT_RETRY_TEMPLATE)
                .observationRegistry(SpringUtil.getBean(ObservationRegistry.class))
                .build();
    }
    /**
     * å¯å‚考 {@link OpenAiImageAutoConfiguration} çš„ openAiImageModel æ–¹æ³•
     */
    private OpenAiImageModel buildOpenAiImageModel(String openAiToken, String url) {
        return new OpenAiImageModel(buildOpenAiClient(openAiToken, url));
        OpenAiImageOptions.Builder optionsBuilder = OpenAiImageOptions.builder().apiKey(openAiToken);
        if (StrUtil.isNotEmpty(url)) {
            optionsBuilder.baseUrl(url);
        }
        return OpenAiImageModel.builder()
                .options(optionsBuilder.build())
                .build();
    }
    /**
@@ -571,7 +519,6 @@
        OllamaApi ollamaApi = OllamaApi.builder().baseUrl(url).build();
        return OllamaChatModel.builder()
                .ollamaApi(ollamaApi)
                .toolCallingManager(getToolCallingManager())
                .build();
    }
@@ -597,44 +544,7 @@
     * å¯å‚考 {@link DashScopeEmbeddingAutoConfiguration} çš„ DashScopeEmbeddingModel æ–¹æ³•
     */
    private DashScopeEmbeddingModel buildTongYiEmbeddingModel(String apiKey, String model) {
        DashScopeApi dashScopeApi = DashScopeApi.builder().apiKey(apiKey).build();
        DashScopeEmbeddingOptions dashScopeEmbeddingOptions = DashScopeEmbeddingOptions.builder().model(model).build();
        return new DashScopeEmbeddingModel(dashScopeApi, MetadataMode.EMBED, dashScopeEmbeddingOptions);
    }
    /**
     * å¯å‚考 {@link ZhiPuAiEmbeddingAutoConfiguration} çš„ ZhiPuAiEmbeddingModel æ–¹æ³•
     */
    private ZhiPuAiEmbeddingModel buildZhiPuEmbeddingModel(String apiKey, String url, String model) {
        ZhiPuAiApi.Builder zhiPuAiApiBuilder = ZhiPuAiApi.builder().apiKey(apiKey);
        if (StrUtil.isNotEmpty(url)) {
            zhiPuAiApiBuilder.baseUrl(url);
        }
        ZhiPuAiEmbeddingOptions zhiPuAiEmbeddingOptions = ZhiPuAiEmbeddingOptions.builder().model(model).build();
        return new ZhiPuAiEmbeddingModel(zhiPuAiApiBuilder.build(), MetadataMode.EMBED, zhiPuAiEmbeddingOptions);
    }
    /**
     * å¯å‚考 {@link MiniMaxEmbeddingAutoConfiguration} çš„ miniMaxEmbeddingModel æ–¹æ³•
     */
    private EmbeddingModel buildMiniMaxEmbeddingModel(String apiKey, String url, String model) {
        MiniMaxApi miniMaxApi = StrUtil.isEmpty(url)? new MiniMaxApi(apiKey)
                : new MiniMaxApi(url, apiKey);
        MiniMaxEmbeddingOptions miniMaxEmbeddingOptions = MiniMaxEmbeddingOptions.builder().model(model).build();
        return new MiniMaxEmbeddingModel(miniMaxApi, MetadataMode.EMBED, miniMaxEmbeddingOptions);
    }
    /**
     * å¯å‚考 {@link QianFanEmbeddingModel} çš„ qianFanEmbeddingModel æ–¹æ³•
     */
    private QianFanEmbeddingModel buildYiYanEmbeddingModel(String key, String model) {
        List<String> keys = StrUtil.split(key, '|');
        Assert.equals(keys.size(), 2, "YiYanChatClient çš„密钥需要 (appKey|secretKey) æ ¼å¼");
        String appKey = keys.get(0);
        String secretKey = keys.get(1);
        QianFanApi qianFanApi = new QianFanApi(appKey, secretKey);
        QianFanEmbeddingOptions qianFanEmbeddingOptions = QianFanEmbeddingOptions.builder().model(model).build();
        return new QianFanEmbeddingModel(qianFanApi, MetadataMode.EMBED, qianFanEmbeddingOptions);
        return AiAutoConfiguration.buildTongYiEmbeddingModel(apiKey, model);
    }
    private OllamaEmbeddingModel buildOllamaEmbeddingModel(String url, String model) {
@@ -642,7 +552,7 @@
        OllamaEmbeddingOptions ollamaOptions = OllamaEmbeddingOptions.builder().model(model).build();
        return OllamaEmbeddingModel.builder()
                .ollamaApi(ollamaApi)
                .defaultOptions(ollamaOptions)
                .options(ollamaOptions)
                .build();
    }
@@ -650,31 +560,31 @@
     * å¯å‚考 {@link OpenAiEmbeddingAutoConfiguration} çš„ openAiEmbeddingModel æ–¹æ³•
     */
    private OpenAiEmbeddingModel buildOpenAiEmbeddingModel(String openAiToken, String url, String model) {
        OpenAiEmbeddingOptions openAiEmbeddingProperties = OpenAiEmbeddingOptions.builder().model(model).build();
        return new OpenAiEmbeddingModel(buildOpenAiClient(openAiToken, url), MetadataMode.EMBED, openAiEmbeddingProperties);
    }
    private static OpenAIClient buildOpenAiClient(String apiKey, String url) {
        OpenAIOkHttpClient.Builder builder = OpenAIOkHttpClient.builder().apiKey(apiKey);
        OpenAiEmbeddingOptions.Builder optionsBuilder = OpenAiEmbeddingOptions.builder()
                .apiKey(openAiToken)
                .model(model);
        if (StrUtil.isNotEmpty(url)) {
            builder.baseUrl(url);
            optionsBuilder.baseUrl(url);
        }
        return builder.build();
        return OpenAiEmbeddingModel.builder()
                .metadataMode(MetadataMode.EMBED)
                .options(optionsBuilder.build())
                .build();
    }
    /**
     * å¯å‚考 {@link AzureOpenAiEmbeddingAutoConfiguration} çš„ azureOpenAiEmbeddingModel æ–¹æ³•
     */
    private AzureOpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String apiKey, String url, String model) {
        // TODO @芋艿:手头暂时没密钥,使用建议再测试下
        AzureOpenAiEmbeddingAutoConfiguration azureOpenAiAutoConfiguration = new AzureOpenAiEmbeddingAutoConfiguration();
        // åˆ›å»º OpenAIClientBuilder å¯¹è±¡
        OpenAIClientBuilder openAIClientBuilder = new OpenAIClientBuilder()
                .endpoint(url).credential(new KeyCredential(apiKey));
        // èŽ·å– AzureOpenAiChatProperties å¯¹è±¡
        AzureOpenAiEmbeddingProperties embeddingProperties = SpringUtil.getBean(AzureOpenAiEmbeddingProperties.class);
        return azureOpenAiAutoConfiguration.azureOpenAiEmbeddingModel(openAIClientBuilder, embeddingProperties,
                getObservationRegistry(), getEmbeddingModelObservationConvention());
    private OpenAiEmbeddingModel buildAzureOpenAiEmbeddingModel(String openAiToken, String url, String model) {
        OpenAiEmbeddingOptions.Builder optionsBuilder = OpenAiEmbeddingOptions.builder()
                .apiKey(openAiToken)
                .model(model)
                .deploymentName(model)
                .azure(true);
        if (StrUtil.isNotEmpty(url)) {
            optionsBuilder.baseUrl(url);
        }
        return OpenAiEmbeddingModel.builder()
                .metadataMode(MetadataMode.EMBED)
                .options(optionsBuilder.build())
                .build();
    }
    // ========== å„种创建 VectorStore çš„æ–¹æ³• ==========
@@ -737,13 +647,11 @@
     */
    private RedisVectorStore buildRedisVectorStore(EmbeddingModel embeddingModel,
                                                   Map<String, Class<?>> metadataFields) {
        // åˆ›å»º JedisPooled å¯¹è±¡
        DataRedisProperties redisProperties = SpringUtils.getBean(DataRedisProperties.class);
        JedisPooled jedisPooled = new JedisPooled(redisProperties.getHost(), redisProperties.getPort(),
                redisProperties.getUsername(), redisProperties.getPassword());
        // åˆ›å»º RedisClient å¯¹è±¡
        RedisClient redisClient = buildRedisClient();
        // åˆ›å»º RedisVectorStoreProperties å¯¹è±¡
        RedisVectorStoreProperties properties = SpringUtil.getBean(RedisVectorStoreProperties.class);
        RedisVectorStore redisVectorStore = RedisVectorStore.builder(jedisPooled, embeddingModel)
        RedisVectorStore redisVectorStore = RedisVectorStore.builder(redisClient, embeddingModel)
                .indexName(properties.getIndexName()).prefix(properties.getPrefix())
                .initializeSchema(properties.isInitializeSchema())
                .metadataFields(convertList(metadataFields.entrySet(), entry -> {
@@ -764,6 +672,43 @@
        // åˆå§‹åŒ–索引
        redisVectorStore.afterPropertiesSet();
        return redisVectorStore;
    }
    private RedisClient buildRedisClient() {
        DataRedisProperties redisProperties = SpringUtil.getBean(DataRedisProperties.class);
        Assert.isNull(redisProperties.getCluster(), "RedisVectorStore æš‚不支持 Redis Cluster æ¨¡å¼");
        Assert.isNull(redisProperties.getSentinel(), "RedisVectorStore æš‚不支持 Redis Sentinel æ¨¡å¼");
        Assert.isNull(redisProperties.getMasterreplica(), "RedisVectorStore æš‚不支持 Redis Master-Replica æ¨¡å¼");
        if (StrUtil.isNotEmpty(redisProperties.getUrl())) {
            return RedisClient.create(redisProperties.getUrl());
        }
        DefaultJedisClientConfig.Builder clientConfigBuilder = DefaultJedisClientConfig.builder()
                .ssl(redisProperties.getSsl().isEnabled())
                .database(redisProperties.getDatabase());
        if (StrUtil.isNotEmpty(redisProperties.getUsername())) {
            clientConfigBuilder.user(redisProperties.getUsername());
        }
        if (StrUtil.isNotEmpty(redisProperties.getPassword())) {
            clientConfigBuilder.password(redisProperties.getPassword());
        }
        if (StrUtil.isNotEmpty(redisProperties.getClientName())) {
            clientConfigBuilder.clientName(redisProperties.getClientName());
        }
        if (redisProperties.getTimeout() != null) {
            clientConfigBuilder.socketTimeoutMillis(toMillis(redisProperties.getTimeout()));
        }
        if (redisProperties.getConnectTimeout() != null) {
            clientConfigBuilder.connectionTimeoutMillis(toMillis(redisProperties.getConnectTimeout()));
        }
        JedisClientConfig clientConfig = clientConfigBuilder.build();
        return RedisClient.builder()
                .hostAndPort(new HostAndPort(redisProperties.getHost(), redisProperties.getPort()))
                .clientConfig(clientConfig)
                .build();
    }
    private static int toMillis(Duration duration) {
        return Math.toIntExact(duration.toMillis());
    }
    /**
@@ -825,10 +770,6 @@
    private static BatchingStrategy getBatchingStrategy() {
        return SpringUtil.getBean(BatchingStrategy.class);
    }
    private static ToolCallingManager getToolCallingManager() {
        return SpringUtil.getBean(ToolCallingManager.class);
    }
    private static ObjectProvider<EmbeddingModelObservationConvention> getEmbeddingModelObservationConvention() {
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/baichuan/BaiChuanChatModel.java
@@ -6,7 +6,7 @@
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import reactor.core.publisher.Flux;
/**
@@ -20,26 +20,35 @@
    public static final String BASE_URL = "https://api.baichuan-ai.com";
    public static final String MODEL_DEFAULT = "Baichuan4-Turbo";
    public static final String COMPLETE_PATH = "/v1/chat/completions";
    public static final String MODEL_DEFAULT = "Baichuan-M3";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œè¿›è¡Œå¤ç”¨
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œå¤ç”¨ DeepSeek å®¢æˆ·ç«¯
     */
    private final OpenAiChatModel openAiChatModel;
    private final DeepSeekChatModel deepSeekChatModel;
    @Override
    public ChatResponse call(Prompt prompt) {
        return openAiChatModel.call(prompt);
        return deepSeekChatModel.call(prompt);
    }
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return openAiChatModel.stream(prompt);
        return deepSeekChatModel.stream(prompt);
    }
    @Override
    public ChatOptions getOptions() {
        return deepSeekChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return openAiChatModel.getDefaultOptions();
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/doubao/DouBaoChatModel.java
@@ -20,7 +20,7 @@
    public static final String BASE_URL = "https://ark.cn-beijing.volces.com/api";
    public static final String COMPLETE_PATH = "/v3/chat/completions";
    public static final String MODEL_DEFAULT = "doubao-1-5-lite-32k-250115";
    public static final String MODEL_DEFAULT = "doubao-seed-2-1-turbo-260628";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œè¿›è¡Œå¤ç”¨
@@ -38,8 +38,15 @@
    }
    @Override
    public ChatOptions getOptions() {
        return openAiChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return openAiChatModel.getDefaultOptions();
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/gemini/GeminiChatModel.java
ÎļþÒÑɾ³ý
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/grok/GrokChatModel.java
@@ -17,9 +17,9 @@
@RequiredArgsConstructor
public class GrokChatModel implements ChatModel {
    public static final String BASE_URL = "https://api.x.ai";
    public static final String BASE_URL = "https://api.x.ai/v1";
    public static final String COMPLETE_PATH = "/v1/chat/completions";
    public static final String MODEL_DEFAULT = "grok-4-fast-reasoning";
    public static final String MODEL_DEFAULT = "grok-4.3";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œè¿›è¡Œå¤ç”¨
@@ -37,8 +37,15 @@
    }
    @Override
    public ChatOptions getOptions() {
        return openAiChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return openAiChatModel.getDefaultOptions();
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/hunyuan/HunYuanChatModel.java
@@ -9,10 +9,9 @@
import reactor.core.publisher.Flux;
/**
 * è…¾äº‘æ··å…ƒ {@link ChatModel} å®žçŽ°ç±»
 * è…¾è®¯æ··å…ƒ {@link ChatModel} å®žçŽ°ç±»
 *
 * 1. æ··å…ƒå¤§æ¨¡åž‹ï¼šåŸºäºŽ <a href="https://cloud.tencent.com/document/product/1729/111007">知识引擎原子能力</a> å®žçް
 * 2. çŸ¥è¯†å¼•擎原子能力:基于 <a href="https://cloud.tencent.com/document/product/1772/115969">知识引擎原子能力</a> å®žçް
 * åŸºäºŽ <a href="https://cloud.tencent.com/document/product/1823/132252">TokenHub OpenAI å…¼å®¹æŽ¥å£</a> å®žçް
 *
 * @author fansili
 */
@@ -20,14 +19,10 @@
@RequiredArgsConstructor
public class HunYuanChatModel implements ChatModel {
    public static final String BASE_URL = "https://api.hunyuan.cloud.tencent.com";
    public static final String BASE_URL = "https://tokenhub.tencentmaas.com";
    public static final String COMPLETE_PATH = "/v1/chat/completions";
    public static final String MODEL_DEFAULT = "hunyuan-turbo";
    public static final String DEEP_SEEK_BASE_URL = "https://api.lkeap.cloud.tencent.com";
    public static final String DEEP_SEEK_MODEL_DEFAULT = "deepseek-v3";
    public static final String MODEL_DEFAULT = "hy3-preview";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œè¿›è¡Œå¤ç”¨
@@ -45,8 +40,15 @@
    }
    @Override
    public ChatOptions getOptions() {
        return openAiChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return openAiChatModel.getDefaultOptions();
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/minimax/MiniMaxChatModel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import reactor.core.publisher.Flux;
/**
 * MiniMax {@link ChatModel} å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Slf4j
@RequiredArgsConstructor
public class MiniMaxChatModel implements ChatModel {
    public static final String BASE_URL = "https://api.minimaxi.com/v1";
    public static final String MODEL_DEFAULT = "MiniMax-M3";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œå¤ç”¨ DeepSeek å®¢æˆ·ç«¯
     */
    private final DeepSeekChatModel deepSeekChatModel;
    @Override
    public ChatResponse call(Prompt prompt) {
        return deepSeekChatModel.call(prompt);
    }
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return deepSeekChatModel.stream(prompt);
    }
    @Override
    public ChatOptions getOptions() {
        return deepSeekChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/moonshot/MoonshotChatModel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import reactor.core.publisher.Flux;
/**
 * æœˆä¹‹æš—面 {@link ChatModel} å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Slf4j
@RequiredArgsConstructor
public class MoonshotChatModel implements ChatModel {
    public static final String BASE_URL = "https://api.moonshot.cn";
    public static final String COMPLETE_PATH = "/v1/chat/completions";
    public static final String MODEL_DEFAULT = "kimi-k2.6";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œå¤ç”¨ DeepSeek å®¢æˆ·ç«¯
     */
    private final DeepSeekChatModel deepSeekChatModel;
    @Override
    public ChatResponse call(Prompt prompt) {
        return deepSeekChatModel.call(prompt);
    }
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return deepSeekChatModel.stream(prompt);
    }
    @Override
    public ChatOptions getOptions() {
        return deepSeekChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowApiConstants.java
@@ -25,7 +25,7 @@
    public static final String DEFAULT_BASE_URL = "https://api.siliconflow.cn";
    public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-R1-Distill-Qwen-7B";
    public static final String MODEL_DEFAULT = "deepseek-ai/DeepSeek-V4-Pro";
    public static final String DEFAULT_IMAGE_MODEL = "Kwai-Kolors/Kolors";
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowChatModel.java
@@ -6,7 +6,6 @@
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import reactor.core.publisher.Flux;
/**
@@ -36,8 +35,15 @@
    }
    @Override
    public ChatOptions getOptions() {
        return openAiChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return openAiChatModel.getDefaultOptions();
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/siliconflow/SiliconFlowImageModel.java
@@ -29,9 +29,9 @@
import org.springframework.ai.openai.OpenAiImageModel;
import org.springframework.ai.openai.metadata.OpenAiImageGenerationMetadata;
import org.springframework.ai.retry.RetryUtils;
import org.springframework.core.retry.RetryTemplate;
import org.springframework.http.ResponseEntity;
import org.jspecify.annotations.Nullable;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.util.Assert;
import java.util.List;
@@ -95,8 +95,8 @@
            .observation(this.observationConvention, DEFAULT_OBSERVATION_CONVENTION, () -> observationContext,
                    this.observationRegistry)
            .observe(() -> {
                ResponseEntity<SiliconFlowImageApi.SiliconFlowImageResponse> imageResponseEntity = this.retryTemplate
                    .execute(ctx -> this.siliconFlowImageApi.createImage(imageRequest));
                    ResponseEntity<SiliconFlowImageApi.SiliconFlowImageResponse> imageResponseEntity = RetryUtils.execute(
                            this.retryTemplate, () -> this.siliconFlowImageApi.createImage(imageRequest));
                ImageResponse imageResponse = convertResponse(imageResponseEntity, imageRequest);
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/stepfun/StepFunChatModel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import reactor.core.publisher.Flux;
/**
 * é˜¶è·ƒæ˜Ÿè¾° {@link ChatModel} å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Slf4j
@RequiredArgsConstructor
public class StepFunChatModel implements ChatModel {
    public static final String BASE_URL = "https://api.stepfun.com";
    public static final String COMPLETE_PATH = "/v1/chat/completions";
    public static final String MODEL_DEFAULT = "step-3.7-flash";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œå¤ç”¨ DeepSeek å®¢æˆ·ç«¯
     */
    private final DeepSeekChatModel deepSeekChatModel;
    @Override
    public ChatResponse call(Prompt prompt) {
        return deepSeekChatModel.call(prompt);
    }
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return deepSeekChatModel.stream(prompt);
    }
    @Override
    public ChatOptions getOptions() {
        return deepSeekChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/xinghuo/XingHuoChatModel.java
@@ -1,11 +1,14 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo;
import lombok.RequiredArgsConstructor;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
/**
@@ -14,37 +17,144 @@
 * @author fansili
 */
@Slf4j
@RequiredArgsConstructor
public class XingHuoChatModel implements ChatModel {
    public static final String BASE_URL_V1 = "https://spark-api-open.xf-yun.com";
    public static final String BASE_URL_V2 = "https://spark-api-open.xf-yun.com";
    public static final String BASE_COMPLETIONS_PATH_V2 = "/v2/chat/completions";
    /**
     * æ˜Ÿç« X2
     *
     * @see <a href="https://spark-api-open.xf-yun.com/x2/chat/completions">接口地址</a>
     */
    public static final String MODEL_X2 = "x2";
    /**
     * å·²çŸ¥æ¨¡åž‹ååˆ—表:x1、4.0Ultra、generalv3.5、max-32k、generalv3、pro-128k、lite
     * æ˜Ÿç« X2 Flash
     *
     * @see <a href="https://spark-api-open.xf-yun.com/agent/v1/chat/completions">接口地址</a>
     */
    public static final String MODEL_DEFAULT = "4.0Ultra";
    public static final String MODEL_X2_FLASH = "x2-flash";
    private static final String BASE_URL_X2 = "https://spark-api-open.xf-yun.com/x2";
    private static final String BASE_URL_X2_FLASH = "https://spark-api-open.xf-yun.com/agent/v1";
    public static final String MODEL_DEFAULT = MODEL_X2_FLASH;
    private static String getBaseUrl(String model) {
        if (MODEL_X2_FLASH.equals(model)) {
            return BASE_URL_X2_FLASH;
        }
        return BASE_URL_X2;
    }
    private final String apiKey;
    private final DeepSeekChatOptions options;
    /**
     * v1 å…¼å®¹ OpenAI æŽ¥å£ï¼Œè¿›è¡Œå¤ç”¨
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œè¿›è¡Œå¤ç”¨
     */
    private final ChatModel openAiChatModelV1;
    private final ChatModel chatModelX2;
    private final ChatModel chatModelX2Flash;
    private XingHuoChatModel(String apiKey, DeepSeekChatOptions options) {
        this.apiKey = apiKey;
        this.options = options;
        this.chatModelX2 = buildChatModel(MODEL_X2);
        this.chatModelX2Flash = buildChatModel(MODEL_X2_FLASH);
    }
    public static Builder builder() {
        return new Builder();
    }
    @Override
    public ChatResponse call(Prompt prompt) {
        return openAiChatModelV1.call(prompt);
        return getChatModel(prompt).call(buildApiPrompt(prompt));
    }
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return openAiChatModelV1.stream(prompt);
        return getChatModel(prompt).stream(buildApiPrompt(prompt));
    }
    @Override
    public ChatOptions getOptions() {
        return options;
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return openAiChatModelV1.getDefaultOptions();
        return getOptions();
    }
    private ChatModel getChatModel(Prompt prompt) {
        String model = options.getModel();
        ChatOptions options = prompt.getOptions();
        if (options != null && isBusinessModel(options.getModel())) {
            model = options.getModel();
        }
        return getChatModel(model);
    }
    private ChatModel getChatModel(String model) {
        if (MODEL_X2_FLASH.equals(model)) {
            return chatModelX2Flash;
        }
        return chatModelX2;
    }
    private ChatModel buildChatModel(String model) {
        return DeepSeekChatModel.builder()
                .deepSeekApi(DeepSeekApi.builder()
                        .baseUrl(getBaseUrl(model))
                        .apiKey(apiKey)
                        .build())
                .options(options.mutate().model("spark-x").build())
                .build();
    }
    private static Prompt buildApiPrompt(Prompt prompt) {
        ChatOptions options = prompt.getOptions();
        if (options == null) {
            return prompt;
        }
        return Prompt.builder()
                .messages(prompt.getInstructions())
                .chatOptions(options.mutate().model("spark-x").build())
                .build();
    }
    private static boolean isBusinessModel(String model) {
        return MODEL_X2.equals(model) || MODEL_X2_FLASH.equals(model);
    }
    public static final class Builder {
        private String apiKey;
        private DeepSeekChatOptions options;
        public Builder apiKey(String apiKey) {
            this.apiKey = apiKey;
            return this;
        }
        public Builder options(DeepSeekChatOptions options) {
            this.options = options;
            return this;
        }
        public XingHuoChatModel build() {
            DeepSeekChatOptions options = this.options != null ? this.options : DeepSeekChatOptions.builder().build();
            if (StrUtil.isEmpty(options.getModel())) {
                options = options.mutate().model(MODEL_DEFAULT).build();
            }
            return new XingHuoChatModel(apiKey, options);
        }
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/yiyan/YiYanChatModel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import reactor.core.publisher.Flux;
/**
 * æ–‡å¿ƒä¸€è¨€ {@link ChatModel} å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Slf4j
@RequiredArgsConstructor
public class YiYanChatModel implements ChatModel {
    public static final String BASE_URL = "https://qianfan.baidubce.com/v2";
    public static final String MODEL_DEFAULT = "ernie-5.1";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œå¤ç”¨ DeepSeek å®¢æˆ·ç«¯
     */
    private final DeepSeekChatModel deepSeekChatModel;
    @Override
    public ChatResponse call(Prompt prompt) {
        return deepSeekChatModel.call(prompt);
    }
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return deepSeekChatModel.stream(prompt);
    }
    @Override
    public ChatOptions getOptions() {
        return deepSeekChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/zhipu/ZhiPuChatModel.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import reactor.core.publisher.Flux;
/**
 * æ™ºè°± {@link ChatModel} å®žçŽ°ç±»
 *
 * @author èŠ‹é“æºç 
 */
@Slf4j
@RequiredArgsConstructor
public class ZhiPuChatModel implements ChatModel {
    public static final String BASE_URL = "https://open.bigmodel.cn/api/paas/v4";
    public static final String MODEL_DEFAULT = "glm-5.2";
    /**
     * å…¼å®¹ OpenAI æŽ¥å£ï¼Œå¤ç”¨ DeepSeek å®¢æˆ·ç«¯
     */
    private final DeepSeekChatModel deepSeekChatModel;
    @Override
    public ChatResponse call(Prompt prompt) {
        return deepSeekChatModel.call(prompt);
    }
    @Override
    public Flux<ChatResponse> stream(Prompt prompt) {
        return deepSeekChatModel.stream(prompt);
    }
    @Override
    public ChatOptions getOptions() {
        return deepSeekChatModel.getOptions();
    }
    @Override
    @Deprecated(forRemoval = true)
    @SuppressWarnings("removal")
    public ChatOptions getDefaultOptions() {
        return getOptions();
    }
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/image/AiImageServiceImpl.java
@@ -29,14 +29,12 @@
import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springaicommunity.qianfan.QianFanImageOptions;
import org.springframework.ai.image.ImageModel;
import org.springframework.ai.image.ImageOptions;
import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse;
import org.springframework.ai.openai.OpenAiImageOptions;
import org.springframework.ai.stabilityai.api.StabilityAiImageOptions;
import org.springframework.ai.zhipuai.ZhiPuAiImageOptions;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -167,15 +165,6 @@
            return DashScopeImageOptions.builder()
                    .model(model.getModel()).n(1)
                    .height(draw.getHeight()).width(draw.getWidth())
                    .build();
        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.YI_YAN.getPlatform())) {
            return QianFanImageOptions.builder()
                    .model(model.getModel()).N(1)
                    .height(draw.getHeight()).width(draw.getWidth())
                    .build();
        } else if (ObjUtil.equal(model.getPlatform(), AiPlatformEnum.ZHI_PU.getPlatform())) {
            return ZhiPuAiImageOptions.builder()
                    .model(model.getModel())
                    .build();
        }
        throw new IllegalArgumentException("不支持的 AI å¹³å°ï¼š" + model.getPlatform());
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/service/model/AiModelServiceImpl.java
@@ -198,4 +198,4 @@
        }
    }
}
}
yudao-module-ai/src/main/java/cn/iocoder/yudao/module/ai/util/AiUtils.java
@@ -8,22 +8,23 @@
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.ai.enums.model.AiPlatformEnum;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
import org.springaicommunity.moonshot.MoonshotChatOptions;
import org.springaicommunity.qianfan.QianFanChatOptions;
import cn.hutool.extra.spring.SpringUtil;
import org.springframework.ai.anthropic.AnthropicChatOptions;
import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
import org.springframework.ai.chat.messages.*;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.ai.deepseek.DeepSeekAssistantMessage;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.minimax.MiniMaxChatOptions;
import org.springframework.ai.google.genai.GoogleGenAiChatOptions;
import org.springframework.ai.ollama.api.OllamaChatOptions;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
import org.springframework.core.env.Environment;
import java.util.*;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.ai.enums.ErrorCodeConstants.API_CONFIG_PLACEHOLDER_NOT_RESOLVED;
/**
 * Spring AI å·¥å…·ç±»
@@ -36,13 +37,42 @@
    public static final String TOOL_CONTEXT_TENANT_ID = "TENANT_ID";
    /**
     * è§£æž DB ç­‰åŠ¨æ€é…ç½®é‡Œçš„ Spring å ä½ç¬¦ï¼Œä¾‹å¦‚ ${OPENAI_API_KEY}
     *
     * @param value å¾…解析的配置值
     * @return è§£æžåŽçš„配置值
     */
    public static String resolveSpringPlaceholders(String value) {
        if (StrUtil.isBlank(value) || !StrUtil.contains(value, "${")) {
            return value;
        }
        try {
            return SpringUtil.getBean(Environment.class).resolveRequiredPlaceholders(value);
        } catch (IllegalArgumentException ex) {
            throw exception(API_CONFIG_PLACEHOLDER_NOT_RESOLVED, value);
        }
    }
    /**
     * æ ¡éªŒ API Key,避免集成测试使用默认占位值发起调用。
     *
     * @param apiKey API Key
     */
    public static void validateApiKey(String apiKey) {
        if (StrUtil.isBlank(apiKey) || "sk-xxxx".equals(apiKey)) {
            throw new IllegalStateException("apiKey ä¸èƒ½ä¸ºç©º");
        }
    }
    /**
     * é€šä¹‰åƒé—®æ”¯æŒå¤šæ¨¡æ€çš„æ¨¡åž‹
     *
     * @see <a href="https://bailian.console.aliyun.com/cn-beijing/?tab=model#/model-market/all?providers=qwen&capabilities=VU">模型广场</a>
     * @see <a href="https://help.aliyun.com/zh/model-studio/error-code#error-url">必须开启 withMultiModel å‚æ•°</a>
     */
    public static final Set<String> TONG_YI_MULTI_MODELS = SetUtils.asSet(
            // qwen3.5 / 3.6 ç³»åˆ—(统一多模态主干)
            // qwen3.5 / 3.6 / 3.7 ç³»åˆ—(统一多模态主干)
            "qwen3.7-max", "qwen3.7-plus", "qwen3.7-flash",
            "qwen3.6-plus", "qwen3.6-flash",
            "qwen3.5-plus", "qwen3.5-flash",
            // qwen-vl è§†è§‰ç†è§£
@@ -72,32 +102,29 @@
                        .enableThinking(true) // TODO èŠ‹è‰¿ï¼šé»˜è®¤éƒ½å¼€å¯ thinking æ¨¡å¼ï¼ŒåŽç»­å¯ä»¥è®©ç”¨æˆ·é…ç½®
                        .multiModel(TONG_YI_MULTI_MODELS.contains(model)) // æ˜¯å¦å¤šæ¨¡æ€æ¨¡åž‹
                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
            case YI_YAN:
                return QianFanChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens).build();
            case DEEP_SEEK:
            case DOU_BAO: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case HUN_YUAN: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case SILICON_FLOW: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case YI_YAN: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case ZHI_PU: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case XING_HUO: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case MINI_MAX: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case MOONSHOT: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case BAI_CHUAN: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
            case STEP_FUN: // å¤ç”¨ DeepSeek å®¢æˆ·ç«¯
                return DeepSeekChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
            case ZHI_PU:
                return ZhiPuAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
            case MINI_MAX:
                return MiniMaxChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
            case MOONSHOT:
                return MoonshotChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
            case OPENAI:
            case GEMINI: // å¤ç”¨ OpenAI å®¢æˆ·ç«¯
            case BAI_CHUAN: // å¤ç”¨ OpenAI å®¢æˆ·ç«¯
            case GROK: // å¤ç”¨ OpenAI å®¢æˆ·ç«¯
                return OpenAiChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
            case GEMINI:
                return GoogleGenAiChatOptions.builder().model(model).temperature(temperature).maxOutputTokens(maxTokens)
                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
            case AZURE_OPENAI:
                return AzureOpenAiChatOptions.builder().deploymentName(model).temperature(temperature).maxTokens(maxTokens)
                return OpenAiChatOptions.builder().model(model).deploymentName(model).azure(true)
                        .temperature(temperature).maxTokens(maxTokens)
                        .toolCallbacks(toolCallbacks).toolContext(toolContext).build();
            case ANTHROPIC:
                return AnthropicChatOptions.builder().model(model).temperature(temperature).maxTokens(maxTokens)
@@ -159,4 +186,4 @@
        return MapUtil.getStr(output.getMetadata(), "reasoningContent");
    }
}
}
yudao-module-ai/src/main/java/org/springframework/ai/model/tool/DefaultToolExecutionEligibilityPredicate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package org.springframework.ai.model.tool;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import java.lang.reflect.Method;
/**
 * TODO èŠ‹è‰¿ï¼šspring-ai-alibaba 2.0.0-M1.1 ä»ä¾èµ–æ—§çš„ Spring AI ToolExecutionEligibilityPredicate,
 * ä¸´æ—¶è¡¥é½ï¼›å‡çº§åˆ°å…¼å®¹ Spring AI 2.0.0 çš„ spring-ai-alibaba ç‰ˆæœ¬åŽï¼Œåˆ é™¤æœ¬åŒ…下的 shim。
 */
public class DefaultToolExecutionEligibilityPredicate implements ToolExecutionEligibilityPredicate {
    @Override
    public boolean test(ChatOptions promptOptions, ChatResponse chatResponse) {
        return isInternalToolExecutionEnabled(promptOptions) && chatResponse != null && chatResponse.hasToolCalls();
    }
    private static boolean isInternalToolExecutionEnabled(ChatOptions promptOptions) {
        try {
            Method method = promptOptions.getClass().getMethod("getInternalToolExecutionEnabled");
            Object result = method.invoke(promptOptions);
            return result == null || Boolean.TRUE.equals(result);
        } catch (ReflectiveOperationException | SecurityException ignored) {
            return true;
        }
    }
}
yudao-module-ai/src/main/java/org/springframework/ai/model/tool/ToolExecutionEligibilityPredicate.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package org.springframework.ai.model.tool;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.ChatOptions;
import org.springframework.util.Assert;
import java.util.function.BiPredicate;
/**
 * TODO èŠ‹è‰¿ï¼šspring-ai-alibaba 2.0.0-M1.1 ä»ä¾èµ–æ—§çš„ Spring AI ToolExecutionEligibilityPredicate,
 * ä¸´æ—¶è¡¥é½ï¼›å‡çº§åˆ°å…¼å®¹ Spring AI 2.0.0 çš„ spring-ai-alibaba ç‰ˆæœ¬åŽï¼Œåˆ é™¤æœ¬åŒ…下的 shim。
 */
public interface ToolExecutionEligibilityPredicate extends BiPredicate<ChatOptions, ChatResponse> {
    default boolean isToolExecutionRequired(ChatOptions promptOptions, ChatResponse chatResponse) {
        Assert.notNull(promptOptions, "promptOptions cannot be null");
        Assert.notNull(chatResponse, "chatResponse cannot be null");
        return test(promptOptions, chatResponse);
    }
}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AnthropicChatModelTest.java
@@ -1,6 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import com.anthropic.client.okhttp.AnthropicOkHttpClient;
import cn.hutool.system.SystemUtil;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.anthropic.AnthropicChatModel;
@@ -14,6 +14,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link AnthropicChatModel} é›†æˆæµ‹è¯•ç±»
@@ -22,13 +25,18 @@
 */
public class AnthropicChatModelTest {
    private static final String BASE_URL = SystemUtil.get("ANTHROPIC_BASE_URL",
            "https://api.teamorouter.com");
    private static final String API_KEY = SystemUtil.get("ANTHROPIC_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ Anthropic API Key
    private static final String MODEL = SystemUtil.get("ANTHROPIC_MODEL",
            "claude-sonnet-4-6");
    private final AnthropicChatModel chatModel = AnthropicChatModel.builder()
            .anthropicClient(AnthropicOkHttpClient.builder()
                    .apiKey("sk-muubv7cXeLw0Etgs743f365cD5Ea44429946Fa7e672d8942")
                    .baseUrl("https://aihubmix.com")
                    .build())
            .options(AnthropicChatOptions.builder()
                    .model("claude-sonnet-4-5")
                    .baseUrl(BASE_URL)
                    .apiKey(API_KEY)
                    .model(MODEL)
                    .temperature(0.7)
                    .maxTokens(4096)
                    .build())
@@ -37,6 +45,7 @@
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -46,11 +55,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -59,19 +70,25 @@
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(System.out::println).then().block();
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
    // TODO @芋艿:需要等 spring ai å‡çº§ï¼šhttps://github.com/spring-projects/spring-ai/pull/2800
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("thkinking ä¸‹ï¼Œ1+1 ä¸ºä»€ä¹ˆç­‰äºŽ 2 "));
        AnthropicChatOptions options = AnthropicChatOptions.builder()
                .model("claude-sonnet-4-5")
                .temperature(1D)
                .baseUrl(BASE_URL)
                .apiKey(API_KEY)
                .model(MODEL)
                .thinkingEnabled(1024) // https://platform.claude.com/docs/en/build-with-claude/extended-thinking
                .maxTokens(4096)
                .build();
        // è°ƒç”¨
@@ -79,7 +96,7 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/AzureOpenAIChatModelTests.java
@@ -1,44 +1,52 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import com.azure.ai.openai.OpenAIClientBuilder;
import com.azure.core.credential.AzureKeyCredential;
import cn.hutool.system.SystemUtil;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.azure.openai.AzureOpenAiChatModel;
import org.springframework.ai.azure.openai.AzureOpenAiChatOptions;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static org.springframework.ai.model.azure.openai.autoconfigure.AzureOpenAiChatProperties.DEFAULT_DEPLOYMENT_NAME;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link AzureOpenAiChatModel} é›†æˆæµ‹è¯•
 * Azure OpenAI é›†æˆæµ‹è¯•
 *
 * @author èŠ‹é“æºç 
 */
public class AzureOpenAIChatModelTests {
    // TODO @芋艿:晚点在调整
    private final OpenAIClientBuilder openAiApi = new OpenAIClientBuilder()
            .endpoint("https://eastusprejade.openai.azure.com")
            .credential(new AzureKeyCredential("xxx"));
    private final AzureOpenAiChatModel chatModel = AzureOpenAiChatModel.builder()
            .openAIClientBuilder(openAiApi)
            .defaultOptions(AzureOpenAiChatOptions.builder()
                    .deploymentName(DEFAULT_DEPLOYMENT_NAME)
    private static final String BASE_URL = SystemUtil.get("AZURE_OPENAI_BASE_URL",
            "https://xxx.openai.azure.com");
    private static final String API_KEY = SystemUtil.get("AZURE_OPENAI_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ Azure OpenAI API Key
    private static final String DEPLOYMENT_NAME = SystemUtil.get("AZURE_OPENAI_DEPLOYMENT_NAME",
            "gpt-5.4"); // Azure ä¸Šåˆ›å»ºçš„æ¨¡åž‹éƒ¨ç½²åç§°
    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
            .options(OpenAiChatOptions.builder()
                    .baseUrl(BASE_URL)
                    .apiKey(API_KEY)
                    .model(DEPLOYMENT_NAME)
                    .microsoftFoundry(true)
                    .deploymentName(DEPLOYMENT_NAME)
                    .temperature(0.7)
                    .build())
            .build();
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -48,12 +56,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(response.getResult().getOutput());
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -64,7 +73,7 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/BaiChuanChatModelTests.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.baichuan.BaiChuanChatModel;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -9,12 +9,16 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link BaiChuanChatModel} é›†æˆæµ‹è¯•
@@ -23,22 +27,27 @@
 */
public class BaiChuanChatModelTests {
    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
            .openAiClient(OpenAIOkHttpClient.builder()
    private static final String API_KEY = SystemUtil.get("BAICHUAN_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„百川 API Key
    private static final String MODEL = SystemUtil.get("BAICHUAN_MODEL",
            BaiChuanChatModel.MODEL_DEFAULT);
    private final BaiChuanChatModel chatModel = new BaiChuanChatModel(DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(BaiChuanChatModel.BASE_URL)
                    .apiKey("sk-61b6766a94c70786ed02673f5e16af3c") // apiKey
                    .completionsPath(BaiChuanChatModel.COMPLETE_PATH)
                    .apiKey(API_KEY)
                    .build())
            .options(OpenAiChatOptions.builder()
                    .model("Baichuan4-Turbo") // æ¨¡åž‹ï¼ˆhttps://platform.baichuan-ai.com/docs/api)
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL)
                    .temperature(0.7)
                    .build())
            .build();
    private final BaiChuanChatModel chatModel = new BaiChuanChatModel(openAiChatModel);
            .build());
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -48,11 +57,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -61,7 +72,10 @@
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(System.out::println).then().block();
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/CozeChatModelTests.java
@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -9,6 +8,7 @@
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
@@ -23,7 +23,7 @@
public class CozeChatModelTests {
    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
            .openAiClient(OpenAIOkHttpClient.builder()
            .options(OpenAiChatOptions.builder()
                    .baseUrl("http://127.0.0.1:3000")
                    .apiKey("app-4hy2d7fJauSbrKbzTKX1afuP") // apiKey
                    .build())
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DeepSeekChatModelTests.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -14,6 +15,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link DeepSeekChatModel} é›†æˆæµ‹è¯•
@@ -22,12 +26,17 @@
 */
public class DeepSeekChatModelTests {
    private static final String API_KEY = SystemUtil.get("DEEPSEEK_API_KEY",
            "sk-xxxx");
    private static final String MODEL = SystemUtil.get("DEEPSEEK_MODEL",
            "deepseek-v4-flash");
    private final DeepSeekChatModel chatModel = DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .apiKey("sk-eaf4172a057344dd9bc64b1f806b6axx") // apiKey
                    .apiKey(API_KEY) // apiKey
                    .build())
            .defaultOptions(DeepSeekChatOptions.builder()
                    .model("deepseek-chat") // æ¨¡åž‹
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL) // æ¨¡åž‹
                    .temperature(0.7)
                    .build())
            .build();
@@ -35,6 +44,7 @@
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -43,12 +53,13 @@
        // è°ƒç”¨
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -63,11 +74,12 @@
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model("deepseek-reasoner")
                .model(MODEL)
                .build();
        // è°ƒç”¨
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DifyChatModelTests.java
@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -9,6 +8,7 @@
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
@@ -23,7 +23,7 @@
public class DifyChatModelTests {
    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
            .openAiClient(OpenAIOkHttpClient.builder()
            .options(OpenAiChatOptions.builder()
                    .baseUrl("http://127.0.0.1:3000")
                    .apiKey("app-4hy2d7fJauSbrKbzTKX1afuP") // apiKey
                    .build())
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/DouBaoChatModelTests.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -15,6 +16,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link DouBaoChatModel} é›†æˆæµ‹è¯•
@@ -23,19 +27,19 @@
 */
public class DouBaoChatModelTests {
    /**
     * ç›¸æ¯” OpenAIChatModel æ¥è¯´ï¼ŒDeepSeekChatModel å¯ä»¥å…¼å®¹è±†åŒ…çš„ thinking èƒ½åŠ›ï¼
     */
    private static final String API_KEY = SystemUtil.get("DOUBAO_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„豆包 API Key
    private static final String MODEL = SystemUtil.get("DOUBAO_MODEL",
            DouBaoChatModel.MODEL_DEFAULT);
    private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(DouBaoChatModel.BASE_URL)
                    .completionsPath(DouBaoChatModel.COMPLETE_PATH)
                    .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey
                    .apiKey(API_KEY)
                    .build())
            .defaultOptions(DeepSeekChatOptions.builder()
                    .model("doubao-1-5-lite-32k-250115") // æ¨¡åž‹ï¼ˆdoubao)
//                    .model("doubao-seed-1-6-thinking-250715") // æ¨¡åž‹ï¼ˆdoubao)
//                    .model("deepseek-r1-250120") // æ¨¡åž‹ï¼ˆdeepseek)
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL)
                    .temperature(0.7)
                    .build())
            .build();
@@ -45,6 +49,7 @@
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -54,11 +59,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -67,25 +74,26 @@
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(System.out::println).then().block();
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model("doubao-seed-1-6-thinking-250715")
                .build();
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/FastGPTChatModelTests.java
@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -9,6 +8,7 @@
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
@@ -23,7 +23,7 @@
public class FastGPTChatModelTests {
    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
            .openAiClient(OpenAIOkHttpClient.builder()
            .options(OpenAiChatOptions.builder()
                    .baseUrl("https://cloud.fastgpt.cn/api")
                    .apiKey("fastgpt-aqcc61kFtF8CeaglnGAfQOCIDWwjGdJVJHv6hIlMo28otFlva2aZNK") // apiKey
                    .build())
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GeminiChatModelTests.java
@@ -1,7 +1,9 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.gemini.GeminiChatModel;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import cn.hutool.core.util.StrUtil;
import cn.hutool.system.SystemUtil;
import com.google.genai.Client;
import com.google.genai.types.HttpOptions;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -9,36 +11,48 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import org.springframework.ai.google.genai.GoogleGenAiChatModel;
import org.springframework.ai.google.genai.GoogleGenAiChatOptions;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link GeminiChatModel} é›†æˆæµ‹è¯•
 * {@link GoogleGenAiChatModel} é›†æˆæµ‹è¯•
 *
 * @author èŠ‹é“æºç 
 */
public class GeminiChatModelTests {
    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
        .openAiClient(OpenAIOkHttpClient.builder()
                .baseUrl(GeminiChatModel.BASE_URL)
                .apiKey("AIzaSyAVoBxgoFvvte820vEQMma2LKBnC98bqMQ")
                .build())
        .options(OpenAiChatOptions.builder()
                .model(GeminiChatModel.MODEL_DEFAULT) // æ¨¡åž‹
                .temperature(0.7)
                .build())
        .build();
    private static final String BASE_URL = SystemUtil.get("GEMINI_BASE_URL");
    private static final String API_KEY = SystemUtil.get("GEMINI_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ Gemini API Key
    private static final String MODEL = "gemini-3.5-flash";
    private final GeminiChatModel chatModel = new GeminiChatModel(openAiChatModel);
    private final GoogleGenAiChatModel chatModel = GoogleGenAiChatModel.builder()
            .genAiClient(buildClient())
            .options(GoogleGenAiChatOptions.builder()
                    .model(MODEL) // æ¨¡åž‹
                    .temperature(0.7)
                    .build())
            .build();
    private static Client buildClient() {
        Client.Builder builder = Client.builder().apiKey(API_KEY);
        if (StrUtil.isNotBlank(BASE_URL)) {
            builder.httpOptions(HttpOptions.builder().baseUrl(BASE_URL).build());
        }
        return builder.build();
    }
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -48,11 +62,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -61,7 +77,32 @@
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(System.out::println).then().block();
        flux.doOnNext(response -> {
            System.out.println(response);
//            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        GoogleGenAiChatOptions options = GoogleGenAiChatOptions.builder()
                .model(MODEL)
                .thinkingBudget(1024)
                .includeThoughts(true)
                .build();
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
        // æ‰“印结果
        flux.doOnNext(response -> {
            System.out.println(response);
//            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/GrokChatModelTests.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,77 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.grok.GrokChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.ai.openai.OpenAiChatOptions;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link GrokChatModel} é›†æˆæµ‹è¯•
 *
 * @author èŠ‹é“æºç 
 */
public class GrokChatModelTests {
    private static final String API_KEY = SystemUtil.get("GROK_API_KEY",
            "sk-xxxx");
    private static final String MODEL = SystemUtil.get("GROK_MODEL",
            GrokChatModel.MODEL_DEFAULT);
    private final GrokChatModel chatModel = new GrokChatModel(OpenAiChatModel.builder()
            .options(OpenAiChatOptions.builder()
                    .baseUrl(GrokChatModel.BASE_URL)
                    .apiKey(API_KEY)
                    .model(MODEL)
                    .temperature(0.7)
                    .build())
            .build());
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
        messages.add(new UserMessage("1 + 1 = ï¼Ÿ"));
        // è°ƒç”¨
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
        messages.add(new UserMessage("1 + 1 = ï¼Ÿ"));
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/HunYuanChatModelTests.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.hunyuan.HunYuanChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -15,6 +16,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link HunYuanChatModel} é›†æˆæµ‹è¯•
@@ -23,14 +27,19 @@
 */
public class HunYuanChatModelTests {
    private static final String API_KEY = SystemUtil.get("HUNYUAN_API_KEY",
            "sk-xxxx");
    private static final String MODEL = SystemUtil.get("HUNYUAN_MODEL",
            HunYuanChatModel.MODEL_DEFAULT);
    private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(HunYuanChatModel.BASE_URL)
                    .completionsPath(HunYuanChatModel.COMPLETE_PATH)
                    .apiKey("sk-abc") // apiKey
                    .apiKey(API_KEY) // apiKey
                    .build())
            .defaultOptions(DeepSeekChatOptions.builder()
                    .model(HunYuanChatModel.MODEL_DEFAULT) // æ¨¡åž‹
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL) // æ¨¡åž‹
                    .temperature(0.7)
                    .build())
            .build();
@@ -40,6 +49,7 @@
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -48,12 +58,13 @@
        // è°ƒç”¨
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -68,78 +79,16 @@
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model("hunyuan-a13b")
//                .model("hunyuan-turbos-latest")
                .model(MODEL)
                .build();
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages, options));
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
        }).then().block();
    }
    private final DeepSeekChatModel deepSeekOpenAiChatModel = DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(HunYuanChatModel.DEEP_SEEK_BASE_URL)
                    .completionsPath(HunYuanChatModel.COMPLETE_PATH)
                    .apiKey("sk-abc") // apiKey
                    .build())
            .defaultOptions(DeepSeekChatOptions.builder()
//                    .model(HunYuanChatModel.DEEP_SEEK_MODEL_DEFAULT) // æ¨¡åž‹ï¼ˆ"deepseek-v3")
                    .model("deepseek-r1") // æ¨¡åž‹ï¼ˆ"deepseek-r1")
                    .temperature(0.7)
                    .build())
            .build();
    private final HunYuanChatModel deepSeekChatModel = new HunYuanChatModel(deepSeekOpenAiChatModel);
    @Test
    @Disabled
    public void testCall_deepseek() {
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
        messages.add(new UserMessage("1 + 1 = ï¼Ÿ"));
        // è°ƒç”¨
        ChatResponse response = deepSeekChatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
    }
    @Test
    @Disabled
    public void testStream_deepseek() {
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
        messages.add(new UserMessage("1 + 1 = ï¼Ÿ"));
        // è°ƒç”¨
        Flux<ChatResponse> flux = deepSeekChatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(System.out::println).then().block();
    }
    @Test
    @Disabled
    public void testStream_deepseek_thinking() {
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model("deepseek-r1")
                .build();
        // è°ƒç”¨
        Flux<ChatResponse> flux = deepSeekChatModel.stream(new Prompt(messages, options));
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/LlamaChatModelTests.java
@@ -27,7 +27,7 @@
            .ollamaApi(OllamaApi.builder()
                    .baseUrl("http://127.0.0.1:11434")  // Ollama æœåŠ¡åœ°å€
                    .build())
            .defaultOptions(OllamaChatOptions.builder()
            .options(OllamaChatOptions.builder()
                    .model(OllamaModel.LLAMA3.getName()) // æ¨¡åž‹
                    .build())
            .build();
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MiniMaxChatModelTests.java
@@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.minimax.MiniMaxChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -7,13 +9,16 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.minimax.MiniMaxChatModel;
import org.springframework.ai.minimax.MiniMaxChatOptions;
import org.springframework.ai.minimax.api.MiniMaxApi;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link MiniMaxChatModel} çš„集成测试
@@ -22,14 +27,24 @@
 */
public class MiniMaxChatModelTests {
    private final MiniMaxChatModel chatModel = new MiniMaxChatModel(
            new MiniMaxApi("eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJHcm91cE5hbWUiOiLnjovmlofmlowiLCJVc2VyTmFtZSI6IueOi-aWh-aWjCIsIkFjY291bnQiOiIiLCJTdWJqZWN0SUQiOiIxODk3Mjg3MjQ5NDU2ODA4MzQ2IiwiUGhvbmUiOiIxNTYwMTY5MTM5OSIsIkdyb3VwSUQiOiIxODk3Mjg3MjQ5NDQ4NDE5NzM4IiwiUGFnZU5hbWUiOiIiLCJNYWlsIjoiIiwiQ3JlYXRlVGltZSI6IjIwMjUtMDMtMTEgMTI6NTI6MDIiLCJUb2tlblR5cGUiOjEsImlzcyI6Im1pbmltYXgifQ.aAuB7gWW_oA4IYhh-CF7c9MfWWxKN49B_HK-DYjXaDwwffhiG-H1571z1WQhp9QytWG-DqgLejneeSxkiq1wQIe3FsEP2wz4BmGBct31LehbJu8ehLxg_vg75Uod1nFAHbm5mZz6JSVLNIlSo87Xr3UtSzJhAXlapEkcqlA4YOzOpKrZ8l5_OJPTORTCmHWZYgJcRS-faNiH62ZnUEHUozesTFhubJHo5GfJCw_edlnmfSUocERV1BjWvenhZ9My-aYXNktcW9WaSj9l6gayV7A0Ium_PL55T9ln1PcI8gayiVUKJGJDoqNyF1AF9_aF9NOKtTnQzwNqnZdlTYH6hw"), // å¯†é’¥
            MiniMaxChatOptions.builder()
                    .model(MiniMaxApi.ChatModel.ABAB_6_5_G_Chat.getValue()) // æ¨¡åž‹
                    .build());
    private static final String API_KEY = SystemUtil.get("MINIMAX_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ MiniMax API Key
    private static final String MODEL = SystemUtil.get("MINIMAX_MODEL",
            MiniMaxChatModel.MODEL_DEFAULT);
    private final MiniMaxChatModel chatModel = new MiniMaxChatModel(DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(MiniMaxChatModel.BASE_URL)
                    .apiKey(API_KEY)
                    .build())
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL)
                    .build())
            .build());
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -39,12 +54,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(response.getResult().getOutput());
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -55,19 +71,19 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
    // TODO @芋艿:暂时没解析 reasoning_content ç»“果,需要等官方修复
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        MiniMaxChatOptions options = MiniMaxChatOptions.builder()
                .model("MiniMax-M1")
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model(MODEL)
                .build();
        // è°ƒç”¨
@@ -75,7 +91,7 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/MoonshotChatModelTests.java
@@ -1,19 +1,24 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.moonshot.MoonshotChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springaicommunity.moonshot.MoonshotChatModel;
import org.springaicommunity.moonshot.MoonshotChatOptions;
import org.springaicommunity.moonshot.api.MoonshotApi;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link MoonshotChatModel} çš„集成测试
@@ -22,18 +27,27 @@
 */
public class MoonshotChatModelTests {
    private final MoonshotChatModel chatModel = MoonshotChatModel.builder()
            .moonshotApi(MoonshotApi.builder()
                    .apiKey("sk-aHYYV1SARscItye5QQRRNbXij4fy65Ee7pNZlC9gsSQnUKXA") // å¯†é’¥
    private static final String API_KEY = SystemUtil.get("MOONSHOT_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ Moonshot API Key
    private static final String MODEL = SystemUtil.get("MOONSHOT_MODEL",
            MoonshotChatModel.MODEL_DEFAULT);
    private final MoonshotChatModel chatModel = new MoonshotChatModel(DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(MoonshotChatModel.BASE_URL)
                    .completionsPath(MoonshotChatModel.COMPLETE_PATH)
                    .apiKey(API_KEY)
                    .build())
            .defaultOptions(MoonshotChatOptions.builder()
                    .model("kimi-k2-0711-preview") // æ¨¡åž‹
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL)
                    .temperature(1D)
                    .build())
            .build();
            .build());
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -43,12 +57,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(response.getResult().getOutput());
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -59,20 +74,19 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
    // TODO @芋艿:暂时没解析 reasoning_content ç»“果,需要等官方修复
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        MoonshotChatOptions options = MoonshotChatOptions.builder()
//                .model("kimi-k2-0711-preview")
                .model("kimi-thinking-preview")
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model(MODEL)
                .build();
        // è°ƒç”¨
@@ -80,7 +94,7 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OllamaChatModelTests.java
@@ -26,7 +26,7 @@
            .ollamaApi(OllamaApi.builder()
                    .baseUrl("http://127.0.0.1:11434") // Ollama æœåŠ¡åœ°å€
                    .build())
            .defaultOptions(OllamaChatOptions.builder()
            .options(OllamaChatOptions.builder()
//                    .model("qwen") // æ¨¡åž‹ï¼ˆhttps://ollama.com/library/qwen)
                    .model("deepseek-r1") // æ¨¡åž‹ï¼ˆhttps://ollama.com/library/deepseek-r1)
                    .build())
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/OpenAIChatModelTests.java
@@ -1,7 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.azure.ai.openai.models.ReasoningEffortValue;
import cn.hutool.system.SystemUtil;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -17,6 +16,8 @@
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link OpenAiChatModel} é›†æˆæµ‹è¯•
 *
@@ -24,14 +25,18 @@
 */
public class OpenAIChatModelTests {
    private static final String BASE_URL = SystemUtil.get("OPENAI_BASE_URL",
            "https://api.teamorouter.com/v1");
    private static final String API_KEY = SystemUtil.get("OPENAI_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ OpenAI API Key
    private static final String MODEL = SystemUtil.get("OPENAI_MODEL",
            "gpt-5.5");
    private final OpenAiChatModel chatModel = OpenAiChatModel.builder()
            .openAiClient(OpenAIOkHttpClient.builder()
                    .baseUrl("https://api.holdai.top")
                    .apiKey("sk-z5joyRoV1iFEnh2SAi8QPNrIZTXyQSyxTmD5CoNDQbFixK2l") // apiKey
                    .build())
            .options(OpenAiChatOptions.builder()
                    .model("gpt-5-nano-2025-08-07") // æ¨¡åž‹
//                    .model("o1") // æ¨¡åž‹
                    .baseUrl(BASE_URL)
                    .apiKey(API_KEY)
                    .model(MODEL)
                    .temperature(0.7)
                    .build())
            .build();
@@ -39,6 +44,7 @@
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -54,6 +60,7 @@
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -72,14 +79,16 @@
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        OpenAiChatOptions options = OpenAiChatOptions.builder()
                .model("gpt-5")
//                .model("o4-mini")
//                .model("o3-pro")
                .reasoningEffort(ReasoningEffortValue.LOW.getValue())
                .baseUrl(BASE_URL)
                .apiKey(API_KEY)
                .model(MODEL)
                .reasoningEffort("low") // https://help.openai.com/zh-hant/articles/5072518-controlling-the-length-of-openai-model-responses
                .maxCompletionTokens(1024)
                .build();
        // è°ƒç”¨
@@ -90,7 +99,5 @@
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/SiliconFlowChatModelTests.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowApiConstants;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowChatModel;
import org.junit.jupiter.api.Disabled;
@@ -16,6 +17,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link SiliconFlowChatModel} é›†æˆæµ‹è¯•
@@ -24,15 +28,22 @@
 */
public class SiliconFlowChatModelTests {
    private static final String API_KEY = SystemUtil.get("SILICONFLOW_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ SiliconFlow API Key
    private static final String MODEL = SystemUtil.get("SILICONFLOW_MODEL",
            SiliconFlowApiConstants.MODEL_DEFAULT);
    private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(SiliconFlowApiConstants.DEFAULT_BASE_URL)
                    .apiKey("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // apiKey
                    .apiKey(API_KEY)
                    .build())
            .defaultOptions(DeepSeekChatOptions.builder()
                    .model(SiliconFlowApiConstants.MODEL_DEFAULT) // æ¨¡åž‹
//                    .model("deepseek-ai/DeepSeek-R1") // æ¨¡åž‹ï¼ˆdeepseek-ai/DeepSeek-R1)可用赠费
//                    .model("Pro/deepseek-ai/DeepSeek-R1") // æ¨¡åž‹ï¼ˆPro/deepseek-ai/DeepSeek-R1)需要付费
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL) // æ¨¡åž‹
//                    .model("deepseek-ai/DeepSeek-V4-Flash") // æ¨¡åž‹
//                    .model("moonshotai/Kimi-K2.7-Code") // æ¨¡åž‹
//                    .model("moonshotai/Kimi-K2.6") // æ¨¡åž‹
//                    .model("zai-org/GLM-5.2") // æ¨¡åž‹
                    .temperature(0.7)
                    .build())
            .build();
@@ -42,6 +53,7 @@
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -56,6 +68,7 @@
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -70,11 +83,12 @@
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model("deepseek-ai/DeepSeek-R1")
                .model(MODEL)
                .build();
        // è°ƒç”¨
@@ -82,7 +96,7 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/StepFunChatModelTests.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.stepfun.StepFunChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link StepFunChatModel} é›†æˆæµ‹è¯•
 *
 * @author èŠ‹é“æºç 
 */
public class StepFunChatModelTests {
    private static final String API_KEY = SystemUtil.get("STEPFUN_API_KEY",
            "sk-xxxx");
    private static final String MODEL = SystemUtil.get("STEPFUN_MODEL",
            StepFunChatModel.MODEL_DEFAULT);
    private final StepFunChatModel chatModel = new StepFunChatModel(DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(StepFunChatModel.BASE_URL)
                    .completionsPath(StepFunChatModel.COMPLETE_PATH)
                    .apiKey(API_KEY)
                    .build())
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL)
                    .temperature(0.7)
                    .build())
            .build());
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
        messages.add(new UserMessage("1 + 1 = ï¼Ÿ"));
        // è°ƒç”¨
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
        messages.add(new UserMessage("1 + 1 = ï¼Ÿ"));
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
//            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
            System.out.println(response.getResult() != null ? response.getResult().getOutput() : null);
        }).then().block();
    }
}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/TongYiChatModelTests.java
@@ -1,6 +1,8 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.module.ai.util.AiUtils;
import com.alibaba.cloud.ai.dashscope.api.DashScopeApi;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatModel;
import com.alibaba.cloud.ai.dashscope.chat.DashScopeChatOptions;
@@ -22,7 +24,9 @@
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
import static java.util.Arrays.asList;
/**
@@ -32,13 +36,18 @@
 */
public class TongYiChatModelTests {
    private static final String API_KEY = SystemUtil.get("DASHSCOPE_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ DashScope API Key
    private static final String MODEL = SystemUtil.get("DASHSCOPE_MODEL",
            "qwen3.7-plus");
    private final DashScopeChatModel chatModel = DashScopeChatModel.builder()
            .dashScopeApi(DashScopeApi.builder()
                    .apiKey("sk-cd9f39e99ea54840bd1888282325f55a") // https://bailian.console.aliyun.com/cn-beijing/?tab=model#/api-key èŽ·å–å¯†é’¥
                    .apiKey(API_KEY)
                    .build())
            .defaultOptions(DashScopeChatOptions.builder()
                    .multiModel(true) // æ³¨æ„ï¼šå½“使用 qwen3.6-plus ç­‰å¤šæ¨¡æ€æ¨¡åž‹ï¼Œéœ€è¦è®¾ç½®ä¸º true,可见 https://help.aliyun.com/zh/model-studio/error-code#error-url é“¾æŽ¥
                    .model("qwen3.6-plus") // æ¨¡åž‹
                    .multiModel(AiUtils.TONG_YI_MULTI_MODELS.contains(MODEL)) // å¤šæ¨¡æ€æ¨¡åž‹éœ€è¦è®¾ç½®ä¸º true,可见 https://help.aliyun.com/zh/model-studio/error-code#error-url
                    .model(MODEL) // æ¨¡åž‹
//                    .model("deepseek-r1") // æ¨¡åž‹ï¼ˆdeepseek-r1)
//                    .model("deepseek-v3") // æ¨¡åž‹ï¼ˆdeepseek-v3)
//                    .model("deepseek-r1-distill-qwen-1.5b") // æ¨¡åž‹ï¼ˆdeepseek-r1-distill-qwen-1.5b)
@@ -49,6 +58,7 @@
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -58,12 +68,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(response.getResult().getOutput());
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
//        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -74,18 +85,19 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        DashScopeChatOptions options = DashScopeChatOptions.builder()
                .model("qwen3.6-plus").multiModel(true)
                .model(MODEL).multiModel(AiUtils.TONG_YI_MULTI_MODELS.contains(MODEL))
//                .withModel("qwen-max-2025-01-25")
                .enableThinking(true) // å¿…须设置,否则会报错
                .build();
@@ -95,17 +107,18 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
    @Test
    @Disabled
    public void testRerank() {
        validateApiKey(API_KEY);
        // å‡†å¤‡çŽ¯å¢ƒ
        RerankModel rerankModel = new DashScopeRerankModel(
                DashScopeApi.builder()
                        .apiKey("sk-47aa124781be4bfb95244cc62f63f7d0")
                        .apiKey(API_KEY)
                        .build());
        // å‡†å¤‡å‚æ•°
        String query = "spring";
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/XingHuoChatModelTests.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.xinghuo.XingHuoChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
@@ -8,13 +9,14 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link XingHuoChatModel} é›†æˆæµ‹è¯•
@@ -23,24 +25,23 @@
 */
public class XingHuoChatModelTests {
    private final DeepSeekChatModel openAiChatModel = DeepSeekChatModel.builder()
        .deepSeekApi(DeepSeekApi.builder()
                .baseUrl(XingHuoChatModel.BASE_URL_V2)
                .completionsPath(XingHuoChatModel.BASE_COMPLETIONS_PATH_V2)
                .apiKey("75b161ed2aef4719b275d6e7f2a4d4cd:YWYxYWI2MTA4ODI2NGZlYTQyNjAzZTcz") // appKey:secretKey
                .build())
        .defaultOptions(DeepSeekChatOptions.builder()
//                .model("generalv3.5") // æ¨¡åž‹
                .model("x1") // æ¨¡åž‹
                .temperature(0.7)
                .build())
        .build();
    private static final String API_KEY = SystemUtil.get("XINGHUO_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„讯飞星火 API Key
    private static final String MODEL = SystemUtil.get("XINGHUO_MODEL",
            XingHuoChatModel.MODEL_DEFAULT);
    private final XingHuoChatModel chatModel = new XingHuoChatModel(openAiChatModel);
    private final XingHuoChatModel chatModel = XingHuoChatModel.builder()
            .apiKey(API_KEY)
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL)
                    .temperature(0.7)
                    .build())
            .build();
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -55,6 +56,7 @@
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -69,11 +71,12 @@
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model("x1")
                .model(MODEL)
                .build();
        // è°ƒç”¨
@@ -81,7 +84,7 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/YiYanChatModelTests.java
@@ -1,62 +1,76 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.yiyan.YiYanChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springaicommunity.qianfan.QianFanChatModel;
import org.springaicommunity.qianfan.QianFanChatOptions;
import org.springaicommunity.qianfan.api.QianFanApi;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
// TODO @芋艿:百度千帆 API æä¾›äº† V2 ç‰ˆæœ¬ï¼Œç›®å‰ Spring AI ä¸å…¼å®¹ï¼Œå¯å…³é”® <https://github.com/spring-projects/spring-ai/issues/2179> è¿›å±•
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link QianFanChatModel} çš„集成测试
 * {@link YiYanChatModel} çš„集成测试
 *
 * @author fansili
 */
public class YiYanChatModelTests {
    private final QianFanChatModel chatModel = new QianFanChatModel(
            new QianFanApi("DGnyzREuaY7av7c38bOM9Ji2", "9aR8myflEOPDrEeLhoXv0FdqANOAyIZW"), // å¯†é’¥
            QianFanChatOptions.builder()
                    .model("ERNIE-4.5-8K-Preview")
                    .build()
    );
    private static final String API_KEY = SystemUtil.get("YIYAN_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„æ–‡å¿ƒä¸€è¨€ API Key
    private static final String MODEL = SystemUtil.get("YIYAN_MODEL",
            YiYanChatModel.MODEL_DEFAULT);
    private final YiYanChatModel chatModel = new YiYanChatModel(DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(YiYanChatModel.BASE_URL)
                    .apiKey(API_KEY)
                    .build())
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL)
                    .build())
            .build());
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        // TODO @芋艿:文心一言,只要带上 system message å°±æŠ¥é”™ï¼Œå·²ç»å„种测试,很莫名!
//        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
        messages.add(new UserMessage("1 + 1 = ï¼Ÿ"));
        // è°ƒç”¨
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        // TODO @芋艿:文心一言,只要带上 system message å°±æŠ¥é”™ï¼Œå·²ç»å„种测试,很莫名!
//        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
        messages.add(new UserMessage("1 + 1 = ï¼Ÿ"));
        // è°ƒç”¨
        Flux<ChatResponse> flux = chatModel.stream(new Prompt(messages));
        // æ‰“印结果
        flux.doOnNext(System.out::println).then().block();
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
}
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/chat/ZhiPuAiChatModelTests.java
@@ -1,5 +1,7 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.chat;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.zhipu.ZhiPuChatModel;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.messages.Message;
@@ -7,31 +9,43 @@
import org.springframework.ai.chat.messages.UserMessage;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.zhipuai.ZhiPuAiChatModel;
import org.springframework.ai.zhipuai.ZhiPuAiChatOptions;
import org.springframework.ai.zhipuai.api.ZhiPuAiApi;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.ai.deepseek.DeepSeekChatOptions;
import org.springframework.ai.deepseek.api.DeepSeekApi;
import reactor.core.publisher.Flux;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link ZhiPuAiChatModel} çš„集成测试
 * {@link ZhiPuChatModel} çš„集成测试
 *
 * @author èŠ‹é“æºç 
 */
public class ZhiPuAiChatModelTests {
    private final ZhiPuAiChatModel chatModel = new ZhiPuAiChatModel(
            ZhiPuAiApi.builder().apiKey("2f35fb6ca4ea41fab898729b7fac086c.6ESSfPcCkxaKEUlR").build(), // å¯†é’¥
            ZhiPuAiChatOptions.builder()
                    .model(ZhiPuAiApi.ChatModel.GLM_4.getName()) // æ¨¡åž‹
    private static final String API_KEY = SystemUtil.get("ZHIPU_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„æ™ºè°± API Key
    private static final String MODEL = SystemUtil.get("ZHIPU_MODEL",
            ZhiPuChatModel.MODEL_DEFAULT);
    private final ZhiPuChatModel chatModel = new ZhiPuChatModel(DeepSeekChatModel.builder()
            .deepSeekApi(DeepSeekApi.builder()
                    .baseUrl(ZhiPuChatModel.BASE_URL)
                    .apiKey(API_KEY)
                    .build())
            .options(DeepSeekChatOptions.builder()
                    .model(MODEL)
                    .build()
    );
            ).build());
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -41,12 +55,13 @@
        ChatResponse response = chatModel.call(new Prompt(messages));
        // æ‰“印结果
        System.out.println(response);
        System.out.println(response.getResult().getOutput());
        System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
    }
    @Test
    @Disabled
    public void testStream() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new SystemMessage("你是一个优质的文言文作者,用文言文描述着各城市的人文风景。"));
@@ -57,19 +72,19 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
    // TODO @芋艿:暂时没解析 reasoning_content ç»“果,需要等官方修复
    @Test
    @Disabled
    public void testStream_thinking() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        List<Message> messages = new ArrayList<>();
        messages.add(new UserMessage("详细分析下,如何设计一个电商系统?"));
        ZhiPuAiChatOptions options = ZhiPuAiChatOptions.builder()
                .model("GLM-4.5")
        DeepSeekChatOptions options = DeepSeekChatOptions.builder()
                .model(MODEL)
                .build();
        // è°ƒç”¨
@@ -77,7 +92,7 @@
        // æ‰“印结果
        flux.doOnNext(response -> {
//            System.out.println(response);
            System.out.println(response.getResult().getOutput());
            System.out.println(Objects.requireNonNull(response.getResult()).getOutput());
        }).then().block();
    }
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/OpenAiImageModelTests.java
@@ -1,6 +1,5 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.image.ImageOptions;
@@ -16,10 +15,12 @@
 */
public class OpenAiImageModelTests {
    private final OpenAiImageModel imageModel = new OpenAiImageModel(OpenAIOkHttpClient.builder()
            .baseUrl("https://api.holdai.top") // apiKey
            .apiKey("sk-PytRecQlmjEteoa2RRN6cGnwslo72UUPLQVNEMS6K9yjbmpD")
            .build());
    private final OpenAiImageModel imageModel = OpenAiImageModel.builder()
            .options(OpenAiImageOptions.builder()
                    .baseUrl("https://api.holdai.top") // apiKey
                    .apiKey("sk-xxx")
                    .build())
            .build();
    @Test
    @Disabled
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/QianFanImageTests.java
ÎļþÒÑɾ³ý
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/SiliconFlowImageModelTests.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageApi;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageModel;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.siliconflow.SiliconFlowImageOptions;
@@ -8,18 +9,24 @@
import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link SiliconFlowImageModel} é›†æˆæµ‹è¯•
 */
public class SiliconFlowImageModelTests {
    private static final String API_KEY = SystemUtil.get("SILICONFLOW_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ SiliconFlow API Key
    private final SiliconFlowImageModel imageModel = new SiliconFlowImageModel(
            new SiliconFlowImageApi("sk-epsakfenqnyzoxhmbucsxlhkdqlcbnimslqoivkshalvdozz") // å¯†é’¥
            new SiliconFlowImageApi(API_KEY)
    );
    @Test
    @Disabled
    public void testCall() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        SiliconFlowImageOptions imageOptions = SiliconFlowImageOptions.builder()
                .model("Kwai-Kolors/Kolors")
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/TongYiImagesModelTest.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.image;
import cn.hutool.system.SystemUtil;
import com.alibaba.cloud.ai.dashscope.api.DashScopeImageApi;
import com.alibaba.cloud.ai.dashscope.image.DashScopeImageModel;
import com.alibaba.cloud.ai.dashscope.image.DashScopeImageOptions;
@@ -8,6 +9,8 @@
import org.springframework.ai.image.ImageOptions;
import org.springframework.ai.image.ImagePrompt;
import org.springframework.ai.image.ImageResponse;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
/**
 * {@link DashScopeImageModel} é›†æˆæµ‹è¯•ç±»
@@ -24,9 +27,12 @@
 */
public class TongYiImagesModelTest {
    private static final String API_KEY = SystemUtil.get("DASHSCOPE_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„ DashScope API Key
    private final DashScopeImageModel imageModel = DashScopeImageModel.builder()
            .dashScopeApi(DashScopeImageApi.builder()
                    .apiKey("sk-cd9f39e99ea54840bd1888282325f55a") // https://bailian.console.aliyun.com/cn-beijing/?tab=model#/api-key èŽ·å–å¯†é’¥
                    .apiKey(API_KEY)
                    .build())
            .build();
@@ -34,6 +40,7 @@
    @Test
    @Disabled
    public void imageCallTest() {
        validateApiKey(API_KEY);
        // å‡†å¤‡å‚æ•°
        ImageOptions options = DashScopeImageOptions.builder()
                .model("wan2.7-image")
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/image/ZhiPuAiImageModelTests.java
ÎļþÒÑɾ³ý
yudao-module-ai/src/test/java/cn/iocoder/yudao/module/ai/framework/ai/core/model/mcp/DouBaoMcpTests.java
@@ -1,7 +1,7 @@
package cn.iocoder.yudao.module.ai.framework.ai.core.model.mcp;
import cn.hutool.system.SystemUtil;
import cn.iocoder.yudao.module.ai.framework.ai.core.model.doubao.DouBaoChatModel;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
@@ -10,16 +10,19 @@
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.method.MethodToolCallbackProvider;
import static cn.iocoder.yudao.module.ai.util.AiUtils.validateApiKey;
@Disabled
public class DouBaoMcpTests {
    private static final String API_KEY = SystemUtil.get("DOUBAO_API_KEY",
            "sk-xxxx"); // æŒ‰éœ€æ”¹æˆä½ çš„豆包 API Key
    private final OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
            .openAiClient(OpenAIOkHttpClient.builder()
                    .baseUrl(DouBaoChatModel.BASE_URL)
                    .apiKey("5c1b5747-26d2-4ebd-a4e0-dd0e8d8b4272") // apiKey
                    .build())
            .options(OpenAiChatOptions.builder()
                    .model("doubao-1-5-lite-32k-250115") // æ¨¡åž‹ï¼ˆdoubao)
                    .baseUrl(DouBaoChatModel.BASE_URL)
                    .apiKey(API_KEY)
                    .model(DouBaoChatModel.MODEL_DEFAULT) // æ¨¡åž‹ï¼ˆdoubao)
                    .temperature(0.7)
                    .build())
            .build();
@@ -36,6 +39,7 @@
    @Test
    public void testMcpGetUserInfo() {
        validateApiKey(API_KEY);
        // æ‰“印结果
        System.out.println(chatClient.prompt()
                .user("目前有哪些工具可以使用")
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/BpmProcessInstanceController.java
@@ -196,6 +196,7 @@
    @GetMapping("/get-bpmn-model-view")
    @Operation(summary = "获取流程实例的 BPMN æ¨¡åž‹è§†å›¾", description = "在【流程详细】界面中,进行调用")
    @Parameter(name = "id", description = "流程实例的编号", required = true)
    @PreAuthorize("@ss.hasPermission('bpm:process-instance:query')")
    public CommonResult<BpmProcessInstanceBpmnModelViewRespVO> getProcessInstanceBpmnModelView(
            @RequestParam(value = "id") String id) {
        return success(processInstanceService.getProcessInstanceBpmnModelView(id));
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/instance/BpmApprovalDetailRespVO.java
@@ -107,6 +107,9 @@
        @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
        private String signPicUrl;
        @Schema(description = "附件", example = "[https://test.yudao.iocoder.cn/20260609/test.txt]")
        private List<String> attachments;
    }
}
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskApproveReqVO.java
@@ -21,6 +21,9 @@
    @Schema(description = "签名", example = "https://www.iocoder.cn/sign.png")
    private String signPicUrl;
    @Schema(description = "附件", example = "[https://test.yudao.iocoder.cn/20260609/test.txt]")
    private List<String> attachments;
    @Schema(description = "变量实例(动态表单)", requiredMode = Schema.RequiredMode.REQUIRED)
    private Map<String, Object> variables;
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/controller/admin/task/vo/task/BpmTaskRejectReqVO.java
@@ -5,6 +5,8 @@
import jakarta.validation.constraints.NotEmpty;
import java.util.List;
@Schema(description = "管理后台 - ä¸é€šè¿‡æµç¨‹ä»»åŠ¡çš„ Request VO")
@Data
public class BpmTaskRejectReqVO {
@@ -16,4 +18,7 @@
    @Schema(description = "审批意见", requiredMode = Schema.RequiredMode.REQUIRED, example = "不错不错!")
    private String reason;
    @Schema(description = "附件", example = "[https://test.yudao.iocoder.cn/20260609/test.txt]")
    private List<String> attachments;
}
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/convert/task/BpmProcessInstanceConvert.java
@@ -9,6 +9,7 @@
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.controller.admin.base.user.UserSimpleBaseVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.BpmModelMetaInfoVO;
import cn.iocoder.yudao.module.bpm.controller.admin.definition.vo.model.simple.BpmSimpleModelNodeVO;
@@ -21,7 +22,6 @@
import cn.iocoder.yudao.module.bpm.convert.definition.BpmProcessDefinitionConvert;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmCategoryDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.api.event.BpmProcessInstanceStatusEvent;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmnModelUtils;
@@ -34,6 +34,7 @@
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Attachment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.mapstruct.Mapper;
@@ -82,7 +83,7 @@
                if (CollUtil.isNotEmpty(respVO.getTasks())) {
                    respVO.getTasks().forEach(task -> {
                        AdminUserRespDTO assigneeUser = userMap.get(task.getAssignee());
                        if (assigneeUser!= null) {
                        if (assigneeUser != null) {
                            task.setAssigneeUser(BeanUtils.toBean(assigneeUser, UserSimpleBaseVO.class));
                            MapUtils.findAndThen(deptMap, assigneeUser.getDeptId(), dept -> task.getAssigneeUser().setDeptName(dept.getName()));
                        }
@@ -137,10 +138,10 @@
    default BpmMessageSendWhenProcessInstanceRejectReqDTO buildProcessInstanceRejectMessage(ProcessInstance instance, String reason) {
        return new BpmMessageSendWhenProcessInstanceRejectReqDTO()
            .setProcessInstanceName(instance.getName())
            .setProcessInstanceId(instance.getId())
            .setReason(reason)
            .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
                .setProcessInstanceName(instance.getName())
                .setProcessInstanceId(instance.getId())
                .setReason(reason)
                .setStartUserId(NumberUtils.parseLong(instance.getStartUserId()));
    }
    default BpmProcessInstanceBpmnModelViewRespVO buildProcessInstanceBpmnModelView(HistoricProcessInstance processInstance,
@@ -183,8 +184,8 @@
    }
    default UserSimpleBaseVO buildUser(Long userId,
                                                    Map<Long, AdminUserRespDTO> userMap,
                                                    Map<Long, DeptRespDTO> deptMap) {
                                       Map<Long, AdminUserRespDTO> userMap,
                                       Map<Long, DeptRespDTO> deptMap) {
        if (userId == null) {
            return null;
        }
@@ -200,13 +201,14 @@
        return userVO;
    }
    default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task) {
    default BpmApprovalDetailRespVO.ActivityNodeTask buildApprovalTaskInfo(HistoricTaskInstance task, List<Attachment> attachments) {
        if (task == null) {
            return null;
        }
        return BeanUtils.toBean(task, BpmApprovalDetailRespVO.ActivityNodeTask.class)
                .setStatus(FlowableUtils.getTaskStatus(task)).setReason(FlowableUtils.getTaskReason(task))
                .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task));
                .setSignPicUrl(FlowableUtils.getTaskSignPicUrl(task))
                .setAttachments(convertList(attachments, Attachment::getUrl));
    }
    default Set<Long> parseUserIds(HistoricProcessInstance processInstance,
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/enums/task/BpmAttachmentTypeEnum.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.bpm.enums.task;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * é™„件类型枚举
 *
 * @author jason
 */
@Getter
@AllArgsConstructor
public enum BpmAttachmentTypeEnum {
    TASK_ATTACHMENT("1", "用户任务附件");
    /**
     * æ“ä½œç±»åž‹
     * <p>
     * ç”±äºŽ BPM attachment ç±»åž‹ä¸º String,所以这里就不使用 Integer
     */
    private final String type;
    /**
     * æ“ä½œåå­—
     */
    private final String name;
}
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/enums/BpmTaskCandidateStrategyEnum.java
@@ -24,7 +24,7 @@
    MULTI_DEPT_LEADER_MULTI(23, "连续多级部门的负责人"),
    POST(22, "岗位"),
    USER(30, "用户"),
    APPROVE_USER_SELECT(34, "审批人自身"), // å½“前审批人,可在审批时,选择下一个节点的审批人
    APPROVE_USER_SELECT(34, "审批人自选"), // å½“前审批人,可在审批时,选择下一个节点的审批人
    START_USER_SELECT(35, "发起人自选"), // ç”³è¯·äººè‡ªå·±ï¼Œå¯åœ¨æäº¤ç”³è¯·æ—¶ï¼Œé€‰æ‹©æ­¤èŠ‚ç‚¹çš„å®¡æ‰¹äºº
    START_USER(36, "发起人自己"), // ç”³è¯·äººè‡ªå·±, ä¸€èˆ¬ç´§æŒ¨å¼€å§‹èŠ‚ç‚¹ï¼Œå¸¸ç”¨äºŽå‘èµ·äººä¿¡æ¯å®¡æ ¸åœºæ™¯
    START_USER_DEPT_LEADER(37, "发起人部门负责人"),
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmHttpRequestUtils.java
@@ -166,7 +166,7 @@
    /**
     * æ·»åŠ  HTTP è¯·æ±‚参数。请求头或者请求体
     *
     * @param params           HTTP è¯·æ±‚参数
     * @param headers          HTTP è¯·æ±‚头
     * @param paramSettings    HTTP è¯·æ±‚参数设置
     * @param processVariables æµç¨‹å˜é‡
     */
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/BpmnModelUtils.java
@@ -457,6 +457,54 @@
    }
    /**
     * æ ¹æ®èŠ‚ç‚¹ï¼Œé€’å½’èŽ·å–ä¸Šæ¸¸ source ä¸º UserTask çš„入口连线
     *
     * 1. å¦‚果当前节点的直接入口连线 source å°±æ˜¯ UserTask,则直接返回该连线
     * 2. å¦‚果当前节点的直接入口连线 source ä¸æ˜¯ UserTask,则继续向上递归查找
     * 3. å¦‚果递归过程中遇到 StartEvent æˆ– SubProcess,则停止该分支继续向上查找
     *
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @return ä¸Šæ¸¸è¿žæŽ¥ UserTask çš„入口连线列表
     */
    public static List<SequenceFlow> getElementIncomingUserTaskFlows(FlowElement source) {
        List<SequenceFlow> result = new ArrayList<>();
        collectElementIncomingUserTaskFlows(source, new HashSet<>(), new HashSet<>(), result);
        return result;
    }
    private static void collectElementIncomingUserTaskFlows(FlowElement source, Set<String> visitedSequenceFlowIds,
                                                            Set<String> resultSequenceFlowIds, List<SequenceFlow> result) {
        // å¦‚果是开始节点或子流程,则停止该分支向上查找
        if (source == null || source instanceof StartEvent || source instanceof SubProcess) {
            return;
        }
        // èŽ·å–å…¥å£è¿žçº¿
        List<SequenceFlow> incomingFlows = getElementIncomingFlows(source);
        if (CollUtil.isEmpty(incomingFlows)) {
            return;
        }
        // å¾ªçŽ¯æ‰¾åˆ°ç›®æ ‡å…ƒç´ 
        for (SequenceFlow incomingFlow : incomingFlows) {
            // å¦‚果发现连线重复,说明连线已经走过。跳过
            if (incomingFlow == null || !visitedSequenceFlowIds.add(incomingFlow.getId())) {
                continue;
            }
            // å¦‚æžœ source æ˜¯ UserTask,则添加到结果中
            FlowElement sourceFlowElement = incomingFlow.getSourceFlowElement();
            if (sourceFlowElement instanceof UserTask) {
                if (resultSequenceFlowIds.add(incomingFlow.getId())) {
                    result.add(incomingFlow);
                }
                continue;
            }
            // é€’归向上查找 UserTask
            collectElementIncomingUserTaskFlows(sourceFlowElement, visitedSequenceFlowIds,
                    resultSequenceFlowIds, result);
        }
    }
    /**
     * æ ¹æ®èŠ‚ç‚¹ï¼ŒèŽ·å–å‡ºå£è¿žçº¿
     *
     * @param source èµ·å§‹èŠ‚ç‚¹
@@ -910,13 +958,88 @@
    }
    /**
     * æŸ¥æ‰¾èµ·å§‹èŠ‚ç‚¹ä¸‹ä¸€ä¸ªç”¨æˆ·ä»»åŠ¡åˆ—è¡¨åˆ—è¡¨
     * æŸ¥æ‰¾èµ·å§‹èŠ‚ç‚¹ä¸‹ä¸€ä¸ªç”¨æˆ·ä»»åŠ¡åˆ—è¡¨, è¯¥æ–¹æ³•会递归向下查找:跳过非 UserTask èŠ‚ç‚¹, æ”¯æŒç½‘关条件判断
     *
     * <ul>
     *     <li>排他网关:走满足条件的唯一一条路径(含默认路径兜底)</li>
     *     <li>包容网关:走所有满足条件的路径</li>
     *     <li>并行网关:走所有出口路径</li>
     * </ul>
     *
     * @param currentElement å½“前节点
     * @param bpmnModel      BPMN æ¨¡åž‹
     * @param variables      æµç¨‹å˜é‡ï¼ˆç”¨äºŽç½‘关条件判断)
     * @return ä¸‹ä¸€ä¸ªç”¨æˆ·ä»»åŠ¡èŠ‚ç‚¹åˆ—è¡¨
     */
    public static List<UserTask> getNextUserTasks(FlowElement currentElement, BpmnModel bpmnModel,
                                                      Map<String, Object> variables) {
        return getNextUserTasks(currentElement, bpmnModel, variables, new HashSet<>(), new ArrayList<>());
    }
    private static List<UserTask> getNextUserTasks(FlowElement currentElement, BpmnModel bpmnModel,
                                                       Map<String, Object> variables,
                                                       Set<String> hasSequenceFlow, List<UserTask> userTaskList) {
        // 1. æ ¹æ®èŠ‚ç‚¹ç±»åž‹å†³å®šè¦éåŽ†çš„å‡ºå£è¿žçº¿
        //    ç½‘关需要根据条件表达式筛选;其它节点直接取所有 outgoing flows
        List<SequenceFlow> outgoingFlows;
        if (currentElement instanceof Gateway) {
            outgoingFlows = getGatewayOutgoingFlows((Gateway) currentElement, variables);
        } else {
            outgoingFlows = getElementOutgoingFlows(currentElement);
        }
        if (CollUtil.isEmpty(outgoingFlows)) {
            return userTaskList;
        }
        // 2. éåŽ†å‡ºå£è¿žçº¿ï¼Œé€’å½’æŸ¥æ‰¾ç”¨æˆ·ä»»åŠ¡
        for (SequenceFlow outgoingFlow : outgoingFlows) {
            // é˜²æ­¢è¿žçº¿æˆçŽ¯å¯¼è‡´æ­»å¾ªçŽ¯
            if (hasSequenceFlow.contains(outgoingFlow.getId())) {
                continue;
            }
            hasSequenceFlow.add(outgoingFlow.getId());
            // èŽ·å–ç›®æ ‡èŠ‚ç‚¹
            FlowElement targetElement = bpmnModel.getFlowElement(outgoingFlow.getTargetRef());
            if (targetElement == null || targetElement instanceof EndEvent) {
                continue;
            }
            if (targetElement instanceof UserTask) {
                // æ‰¾åˆ°ç”¨æˆ·ä»»åŠ¡ï¼šåŠ å…¥ç»“æžœ
                userTaskList.add((UserTask) targetElement);
            } else {
                // éžç”¨æˆ·ä»»åŠ¡ï¼ˆç½‘å…³ã€æœåŠ¡ä»»åŠ¡ã€ä¸­é—´äº‹ä»¶ç­‰ï¼‰ï¼šç»§ç»­é€’å½’å‘ä¸‹æŸ¥æ‰¾
                getNextUserTasks(targetElement, bpmnModel, variables, hasSequenceFlow, userTaskList);
            }
        }
        return userTaskList;
    }
    /**
     * æ ¹æ®ç½‘关类型与流程变量,筛选网关的出口连线
     *
     * @param gateway   ç½‘关节点
     * @param variables æµç¨‹å˜é‡
     * @return å‘½ä¸­çš„出口连线列表
     */
    private static List<SequenceFlow> getGatewayOutgoingFlows(Gateway gateway, Map<String, Object> variables) {
        if (gateway instanceof ExclusiveGateway) {
            SequenceFlow matchFlow = findMatchSequenceFlowByExclusiveGateway(gateway, variables);
            return matchFlow == null ? Collections.emptyList() : Collections.singletonList(matchFlow);
        }
        if (gateway instanceof InclusiveGateway) {
            return new ArrayList<>(findMatchSequenceFlowsByInclusiveGateway(gateway, variables));
        }
        // é»˜è®¤ï¼ˆå¹¶è¡Œç½‘关等):走所有出口
        return gateway.getOutgoingFlows();
    }
    /**
     * æŸ¥æ‰¾èµ·å§‹èŠ‚ç‚¹ä¸‹ä¸€ä¸ªç”¨æˆ·ä»»åŠ¡åˆ—è¡¨åˆ—è¡¨, ä¸åˆ¤æ–­ç½‘关条件
     *
     * @param source èµ·å§‹èŠ‚ç‚¹
     * @return ç»“æžœ
     */
    public static List<UserTask> getNextUserTasks(FlowElement source) {
        return getNextUserTasks(source, null, null);
        return getNextUserTasks(source, new HashSet<>(), new ArrayList<>());
    }
    /**
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtils.java
@@ -25,8 +25,10 @@
import org.flowable.engine.impl.util.CommandContextUtil;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.TaskInfo;
import tools.jackson.databind.JsonNode;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
@@ -245,10 +247,10 @@
        }
        // è§£æžè¡¨å•配置
        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 @@
    /**
     * é€’归解析表单字段
     */
    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);
        }
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
@@ -7,7 +7,6 @@
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.*;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.common.util.object.ObjectUtils;
@@ -25,6 +24,7 @@
import cn.iocoder.yudao.module.bpm.enums.ErrorCodeConstants;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmModelTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmSimpleModelNodeTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmAttachmentTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmProcessInstanceStatusEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
@@ -57,6 +57,7 @@
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceBuilder;
import org.flowable.engine.task.Attachment;
import org.flowable.task.api.Task;
import org.flowable.task.api.history.HistoricTaskInstance;
import org.springframework.context.annotation.Lazy;
@@ -275,11 +276,10 @@
            processVariables.putAll(reqVO.getProcessVariables());
        }
        // 3. èŽ·å–ä¸‹ä¸€ä¸ªå°†è¦æ‰§è¡Œçš„èŠ‚ç‚¹é›†åˆ
        // 3.1 èŽ·å–ä¸‹ä¸€ä¸ªå°†è¦æ‰§è¡Œçš„èŠ‚ç‚¹é›†åˆ
        FlowElement flowElement = bpmnModel.getFlowElement(task.getTaskDefinitionKey());
        List<FlowNode> nextFlowNodes = BpmnModelUtils.getNextFlowNodes(flowElement, bpmnModel, processVariables);
        // ä»…仅获取 UserTask èŠ‚ç‚¹  TODO add from jason:如果网关节点和网关节点相连,获取下个 UserTask. è²Œä¼¼æœ‰ç‚¹ä¸å‡†ã€‚
        List<FlowNode> nextUserTaskList = CollectionUtils.filterList(nextFlowNodes, node -> node instanceof UserTask);
        // 3.2 èŽ·å– UserTask èŠ‚ç‚¹
        List<UserTask> nextUserTaskList = BpmnModelUtils.getNextUserTasks(flowElement, bpmnModel, processVariables);
        List<ActivityNode> nextActivityNodes = convertList(nextUserTaskList, node -> new ActivityNode().setId(node.getId())
                .setName(node.getName()).setNodeType(BpmSimpleModelNodeTypeEnum.APPROVE_NODE.getType())
                .setStatus(BpmTaskStatusEnum.RUNNING.getStatus())
@@ -404,8 +404,13 @@
        // éåކ tasks åˆ—表,只处理已结束的 UserTask
        // ä¸ºä»€ä¹ˆä¸é€šè¿‡ activities å‘¢ï¼Ÿå› ä¸ºï¼ŒåŠ ç­¾åœºæ™¯ä¸‹ï¼Œå®ƒåªå­˜åœ¨äºŽ tasks,没有 activities,导致如果遍历 activities çš„话,它无法成为一个节点
        List<HistoricTaskInstance> endTasks = filterList(tasks, task -> task.getEndTime() != null);
        // èŽ·å–å·²å®ŒæˆèŠ‚ç‚¹çš„é™„ä»¶
        Set<String> endTaskIds = convertSet(endTasks, HistoricTaskInstance::getId);
        List<Attachment> attachments = taskService.getAttachments(historicProcessInstance.getId(), endTaskIds, BpmAttachmentTypeEnum.TASK_ATTACHMENT);
        Map<String, List<Attachment>> taskAttachmentMap = convertMultiMap(attachments, Attachment::getTaskId);
        List<ActivityNode> approvalNodes = convertList(endTasks, task -> {
            FlowElement flowNode = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
            List<Attachment> taskAttachments = taskAttachmentMap.get(task.getId());
            ActivityNode activityNode = new ActivityNode().setId(task.getTaskDefinitionKey()).setName(task.getName())
                    .setNodeType(START_USER_NODE_ID.equals(task.getTaskDefinitionKey())
                            ? BpmSimpleModelNodeTypeEnum.START_USER_NODE.getType()
@@ -414,7 +419,7 @@
                    .setStatus(getEndActivityNodeStatus(task))
                    .setCandidateStrategy(BpmnModelUtils.parseCandidateStrategy(flowNode))
                    .setStartTime(DateUtils.of(task.getCreateTime())).setEndTime(DateUtils.of(task.getEndTime()))
                    .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task)));
                    .setTasks(singletonList(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task, taskAttachments)));
            // å¦‚果是取消状态,则跳过
            if (BpmTaskStatusEnum.isCancelStatus(activityNode.getStatus())) {
                return null;
@@ -528,14 +533,14 @@
                if (task == null) {
                    continue;
                }
                activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task));
                activityNode.getTasks().add(BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(task, null));
                // åŠ ç­¾å­ä»»åŠ¡ï¼Œéœ€è¦è¿‡æ»¤æŽ‰å·²ç»å®Œæˆçš„åŠ ç­¾å­ä»»åŠ¡
                List<HistoricTaskInstance> childrenTasks = filterList(
                        taskService.getAllChildrenTaskListByParentTaskId(activity.getTaskId(), tasks),
                        childTask -> childTask.getEndTime() == null);
                if (CollUtil.isNotEmpty(childrenTasks)) {
                    activityNode.getTasks().addAll(
                            convertList(childrenTasks, BpmProcessInstanceConvert.INSTANCE::buildApprovalTaskInfo));
                            convertList(childrenTasks, item->BpmProcessInstanceConvert.INSTANCE.buildApprovalTaskInfo(item, null)));
                }
            }
            // å¤„理每个任务的 candidateUsers å±žæ€§ï¼šå¦‚果是依次审批,需要预测它的后续审批人。因为 Task æ˜¯å®¡æ‰¹å®Œä¸€ä¸ªï¼Œåˆ›å»ºä¸€ä¸ªæ–°çš„ Task
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskService.java
@@ -4,9 +4,11 @@
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.bpm.controller.admin.task.vo.task.*;
import cn.iocoder.yudao.module.bpm.enums.definition.BpmUserTaskTimeoutHandlerTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmAttachmentTypeEnum;
import jakarta.validation.Valid;
import org.flowable.bpmn.model.UserTask;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.task.Attachment;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
import org.flowable.task.api.history.HistoricTaskInstance;
@@ -14,6 +16,7 @@
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
 * æµç¨‹ä»»åŠ¡å®žä¾‹ Service æŽ¥å£
@@ -184,6 +187,17 @@
     */
    List<HistoricTaskInstance> getFinishedTaskListByProcessInstanceIdWithoutCancel(String processInstanceId);
    /**
     * æ ¹æ®æ¡ä»¶èŽ·å–é™„ä»¶
     *
     * @param processInstanceId æµç¨‹ id
     * @param taskIds ä»»åŠ¡ id é›†åˆ
     * @param attachmentType é™„件类型
     * @return é™„件集合
     */
    List<Attachment> getAttachments(String processInstanceId, Set<String> taskIds, BpmAttachmentTypeEnum attachmentType);
    // ========== Update å†™å…¥ç›¸å…³æ–¹æ³• ==========
    /**
yudao-module-bpm/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmTaskServiceImpl.java
@@ -2,7 +2,9 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.*;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
@@ -19,10 +21,7 @@
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmFormDO;
import cn.iocoder.yudao.module.bpm.dal.dataobject.definition.BpmProcessDefinitionInfoDO;
import cn.iocoder.yudao.module.bpm.enums.definition.*;
import cn.iocoder.yudao.module.bpm.enums.task.BpmCommentTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmReasonEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskSignTypeEnum;
import cn.iocoder.yudao.module.bpm.enums.task.BpmTaskStatusEnum;
import cn.iocoder.yudao.module.bpm.enums.task.*;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmTaskCandidateStrategyEnum;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.util.BpmHttpRequestUtils;
@@ -49,6 +48,7 @@
import org.flowable.engine.runtime.ActivityInstance;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.task.Attachment;
import org.flowable.task.api.DelegationState;
import org.flowable.task.api.Task;
import org.flowable.task.api.TaskInfo;
@@ -510,6 +510,18 @@
                .orderByHistoricTaskInstanceStartTime().asc().list();
    }
    @Override
    public List<Attachment> getAttachments(String processInstanceId, Set<String> taskIds, BpmAttachmentTypeEnum attachmentType) {
        List<Attachment> result = taskService.getProcessInstanceAttachments(processInstanceId);
        if (CollUtil.isNotEmpty(taskIds)) {
            result = filterList(result, attachment -> taskIds.contains(attachment.getTaskId()));
        }
        if (attachmentType != null) {
            result = filterList(result, attachment -> attachmentType.getType().equals(attachment.getType()));
        }
        return result;
    }
    /**
     * åˆ¤æ–­æŒ‡å®šç”¨æˆ·ï¼Œæ˜¯å¦æ˜¯å½“前任务的审批人
     *
@@ -592,6 +604,11 @@
        // 2.2 æ·»åŠ è¯„è®º
        taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.APPROVE.getType(),
                BpmCommentTypeEnum.APPROVE.formatComment(reqVO.getReason()));
        // 2.3 æ·»åР附件
        if (CollUtil.isNotEmpty(reqVO.getAttachments())) {
            reqVO.getAttachments().forEach(attachment -> taskService.createAttachment(BpmAttachmentTypeEnum.TASK_ATTACHMENT.getType(),
                    task.getId(), task.getProcessInstanceId(), FileUtil.getName(URLUtil.getPath(attachment)), null, attachment));
        }
        // 3. è®¾ç½®æµç¨‹å˜é‡ã€‚如果流程变量前端传空,需要从历史实例中获取,原因:前端表单如果在当前节点无可编辑的字段时 variables ä¸€å®šä¼šä¸ºç©º
        // åœºæ™¯ä¸€ï¼šA èŠ‚ç‚¹å‘èµ·ï¼ŒB èŠ‚ç‚¹è¡¨å•æ— å¯ç¼–è¾‘å­—æ®µï¼Œå®¡æ‰¹é€šè¿‡æ—¶ï¼ŒC èŠ‚ç‚¹éœ€è¦æµç¨‹å˜é‡èŽ·å–ä¸‹ä¸€ä¸ªæ‰§è¡ŒèŠ‚ç‚¹ï¼Œä½†å› ä¸º B èŠ‚ç‚¹æ— å¯ç¼–è¾‘çš„å­—æ®µï¼Œvariables ä¸ºç©ºï¼Œæµç¨‹å¯èƒ½å‡ºçŽ°é—®é¢˜ã€‚
@@ -606,9 +623,9 @@
        }
        // 4. æ ¡éªŒå¹¶å¤„理 APPROVE_USER_SELECT å½“前审批人,选择下一节点审批人的逻辑
        Map<String, Object> variables = validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables,
        validateAndSetNextAssignees(task.getTaskDefinitionKey(), processVariables,
                bpmnModel, reqVO.getNextAssignees(), instance);
        runtimeService.setVariables(task.getProcessInstanceId(), variables);
        runtimeService.setVariables(task.getProcessInstanceId(), processVariables);
        // 5. å¦‚果当前节点 Id å­˜åœ¨äºŽéœ€è¦é¢„测的流程节点中,从中移除。 æµç¨‹å˜é‡åœ¨å›žé€€æ“ä½œä¸­è®¾ç½®
        Object needSimulateTaskIds = runtimeService.getVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS);
@@ -618,8 +635,16 @@
            runtimeService.setVariable(task.getProcessInstanceId(), BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskIdsByReturn);
        }
        // 6. è°ƒç”¨ BPM complete åŽ»å®Œæˆä»»åŠ¡
        taskService.complete(task.getId(), variables, true);
        // 6. æ¸…理退回设置的不自动通过的变量。仅在该标记存在时才删除,避免每次完成任务都产生无谓的 DB delete
        String returnFlagKey = String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey());
        if (runtimeService.hasVariable(task.getProcessInstanceId(), returnFlagKey)) {
            log.info("[approveTask][taskId({}) æ¸…理退回标记变量({})]", task.getId(), returnFlagKey);
            runtimeService.removeVariable(task.getProcessInstanceId(), returnFlagKey);
        }
        // 7. è°ƒç”¨ BPM complete åŽ»å®Œæˆä»»åŠ¡
        Map<String,Object> taskVariables = MapUtil.emptyIfNull(reqVO.getVariables()); // task local variables. ä¸€èˆ¬ç”¨äºŽä»»åŠ¡å†…åµŒæµç¨‹è¡¨å•
        taskService.complete(task.getId(), taskVariables, true);
        // ã€åŠ ç­¾ä¸“å±žã€‘å¤„ç†åŠ ç­¾ä»»åŠ¡
        handleParentTaskIfSign(task.getParentTaskId());
@@ -646,10 +671,10 @@
        }
        // 1. èŽ·å–ä¸‹ä¸€ä¸ªå°†è¦æ‰§è¡Œçš„èŠ‚ç‚¹é›†åˆ
        FlowElement flowElement = bpmnModel.getFlowElement(taskDefinitionKey);
        List<FlowNode> nextFlowNodes = getNextFlowNodes(flowElement, bpmnModel, variables);
        List<UserTask> nextFlowNodes = getNextUserTasks(flowElement, bpmnModel, variables);
        // 2. æ ¡éªŒé€‰æ‹©çš„下一个节点的审批人,是否合法
        for (FlowNode nextFlowNode : nextFlowNodes) {
        for (UserTask nextFlowNode : nextFlowNodes) {
            Integer candidateStrategy = parseCandidateStrategy(nextFlowNode);
            // 2.1 æƒ…况一:如果节点中的审批人策略为 å‘起人自选
            if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.START_USER_SELECT.getStrategy())) {
@@ -675,8 +700,12 @@
            // 2.2 æƒ…况二:如果节点中的审批人策略为 å®¡æ‰¹äººï¼Œåœ¨å®¡æ‰¹æ—¶é€‰æ‹©ä¸‹ä¸€ä¸ªèŠ‚ç‚¹çš„å®¡æ‰¹äººï¼Œå¹¶ä¸”è¯¥èŠ‚ç‚¹çš„å®¡æ‰¹äººä¸ºç©º
            if (ObjUtil.equals(candidateStrategy, BpmTaskCandidateStrategyEnum.APPROVE_USER_SELECT.getStrategy())) {
                // å¦‚果节点存在,但未配置审批人
                // ç‰¹æ®Šï¼šå¦‚果当前节点已经存在审批人,则不允许覆盖。 ä¾‹å¦‚并行节点后,设置的审批人自选节点。 https://t.zsxq.com/daxv1
                Map<String, List<Long>> approveUserSelectAssignees = FlowableUtils.getApproveUserSelectAssignees(processInstance.getProcessVariables());
                if (approveUserSelectAssignees != null && CollUtil.isNotEmpty(approveUserSelectAssignees.get(nextFlowNode.getId()))) {
                    continue;
                }
                // å¦‚果节点存在,但未配置审批人
                List<Long> assignees = nextAssignees != null ? nextAssignees.get(nextFlowNode.getId()) : null;
                if (CollUtil.isEmpty(assignees)) {
                    throw exception(PROCESS_INSTANCE_APPROVE_USER_SELECT_ASSIGNEES_NOT_CONFIG, nextFlowNode.getName());
@@ -810,7 +839,13 @@
        // 2.2 æ·»åŠ æµç¨‹è¯„è®º
        taskService.addComment(task.getId(), task.getProcessInstanceId(), BpmCommentTypeEnum.REJECT.getType(),
                BpmCommentTypeEnum.REJECT.formatComment(reqVO.getReason()));
        // 2.3 å¦‚果当前任务时被加签的,则加它的根任务也标记成未通过
        // 2.3 æ·»åР附件
        if (CollUtil.isNotEmpty(reqVO.getAttachments())) {
            reqVO.getAttachments().forEach(attachment -> taskService.createAttachment(BpmAttachmentTypeEnum.TASK_ATTACHMENT.getType(),
                    task.getId(), task.getProcessInstanceId(), FileUtil.getName(URLUtil.getPath(attachment)), null, attachment));
        }
        // 2.4 å¦‚果当前任务时被加签的,则加它的根任务也标记成未通过
        // ç–‘问:为什么要标记未通过呢?
        // å›žç­”:例如说 A ä»»åŠ¡è¢«å‘å‰åŠ ç­¾é™¤ B ä»»åŠ¡æ—¶ï¼ŒB ä»»åŠ¡è¢«å®¡æ‰¹ä¸é€šè¿‡ï¼Œæ­¤æ—¶ A ä¼šè¢«å–消。而 yudao-ui-admin-vue3 ä¸å±•示“已取消”的任务,导致展示不出审批不通过的细节。
        if (task.getParentTaskId() != null) {
@@ -922,14 +957,12 @@
        // ä¸ºä»€ä¹ˆä¸ç›´æŽ¥ä½¿ç”¨ runTaskKeyList å‘¢ï¼Ÿå› ä¸ºå¯èƒ½å­˜åœ¨å¤šä¸ªå®¡æ‰¹åˆ†æ”¯ï¼Œä¾‹å¦‚说:A -> B -> C å’Œ D -> F,而只要 C æ’¤å›žåˆ° A,需要排除掉 F
        List<UserTask> returnUserTaskList = BpmnModelUtils.iteratorFindChildUserTasks(targetElement, runTaskKeyList, null, null);
        List<String> returnTaskKeyList = convertList(returnUserTaskList, UserTask::getId);
        // 2. ç»™å½“前要被退回的 task æ•°ç»„,设置退回意见
        taskList.forEach(task -> {
            // éœ€è¦æŽ’除掉,不需要设置退回意见的任务
            if (!returnTaskKeyList.contains(task.getTaskDefinitionKey())) {
                return;
            }
            // åˆ¤æ–­æ˜¯å¦åˆ†é…ç»™è‡ªå·±ä»»åŠ¡ï¼Œå› ä¸ºä¼šç­¾ä»»åŠ¡ï¼Œä¸€ä¸ªèŠ‚ç‚¹ä¼šæœ‰å¤šä¸ªä»»åŠ¡
            if (isAssignUserTask(userId, task)) { // æƒ…况一:自己的任务,进行 RETURN æ ‡è®°
                // 2.1.1 æ·»åŠ è¯„è®º
@@ -950,14 +983,20 @@
        //    ç›¸å…³ issue: https://github.com/flowable/flowable-engine/issues/3944
        // â‘¡ flowable 7.2.0 ç‰ˆæœ¬åŽï¼Œç»§ç»­ä½¿ç”¨ moveActivityIdsToSingleActivityId æ–¹æ³•。原因:flowable 7.2.0 ç‰ˆæœ¬ä¿®å¤äº†è¯¥é—®é¢˜ã€‚
        //    ç›¸å…³ issue:https://github.com/YunaiV/ruoyi-vue-pro/issues/1018
        // â‘¢ moveActivityIdsToSingleActivityId ä½¿ç”¨é‡åˆ°é—®é¢˜ï¼Œ ç›¸å…³ issue https://gitee.com/zhijiantianya/yudao-cloud/issues/IJM8MS
        //  æ”¹æˆ moveExecutionsToSingleActivityId å¥½åƒå¹¶æ²¡æœ‰é‡åˆ° â‘¡ æåˆ°çš„超时提醒失效的问题。暂时先改回 moveExecutionsToSingleActivityId
        // â‘£ moveExecutionsToSingleActivityId å›žé€€å¤šå®žä¾‹çš„æ—¶å€™ä¸ä¼šåŽ»åˆ é™¤å¤šå®žä¾‹æ ¹, åº”改成 moveActivityIdsToSingleActivityId
        // flowable 8.0.0  ä¿®å¤ä¸Šé¢ç›¸å…³é—®é¢˜ï¼Œ è¿˜ä¿®å¤äº†å¹¶è¡Œåˆ†æ”¯å›žé€€çš„问题 https://t.zsxq.com/z4d9i。
        // â‘¤ ä½¿ç”¨ moveExecutionsToSingleActivityId æ–¹æ³•进行回退操作时,如果是多实例的用户任务【芋道用户任务默认为多实例】,不会删除多实例任务的根数据 ACT_RU_EXECUTION
        // ä¼šå¯¼è‡´æœ‰ä¸€äº›é—®é¢˜ï¼Œæ‰€ä»¥ä½¿ç”¨ moveActivityIdsToSingleActivityId。 ä½†æ˜¯è¯¥æ–¹æ³•在 flowable 6.8.1 ~ 7.1.0 çš„版本会有 bug é˜»å¡žå›žé€€åŠŸèƒ½
        // ç›¸å…³ issue: https://github.com/flowable/flowable-engine/issues/3944
        runtimeService.createChangeActivityStateBuilder()
                .processInstanceId(currentTask.getProcessInstanceId())
                .moveActivityIdsToSingleActivityId(returnTaskKeyList, reqVO.getTargetTaskDefinitionKey())
                // è®¾ç½®éœ€è¦é¢„测的任务 ids çš„æµç¨‹å˜é‡ï¼Œç”¨äºŽè¾…助预测
                .processVariable(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_NEED_SIMULATE_TASK_IDS, needSimulateTaskDefinitionKeys)
                // è®¾ç½®æµç¨‹å˜é‡ï¼ˆlocal)节点退回标记, ç”¨äºŽé€€å›žåˆ°èŠ‚ç‚¹ï¼Œä¸æ‰§è¡Œ BpmUserTaskAssignStartUserHandlerTypeEnum ç­–略,导致自动通过
                .localVariable(reqVO.getTargetTaskDefinitionKey(),
                        String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE)
                // è®¾ç½®æµç¨‹å˜é‡èŠ‚ç‚¹é€€å›žæ ‡è®°, ç”¨äºŽé€€å›žåˆ°èŠ‚ç‚¹ï¼Œä¸æ‰§è¡Œ BpmUserTaskAssignStartUserHandlerTypeEnum ç­–略,导致自动通过
                .processVariable(String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, reqVO.getTargetTaskDefinitionKey()), Boolean.TRUE)
                .changeState();
    }
@@ -1464,101 +1503,105 @@
                    return;
                }
                // è‡ªåŠ¨åŽ»é‡ï¼Œé€šè¿‡è‡ªåŠ¨å®¡æ‰¹çš„æ–¹å¼
                BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId());
                if (processDefinitionInfo == null) {
                    log.error("[processTaskAssigned][taskId({}) æ²¡æœ‰æ‰¾åˆ°æµç¨‹å®šä¹‰({})]", task.getId(), task.getProcessDefinitionId());
                    return;
                }
                if (processDefinitionInfo.getAutoApprovalType() != null) {
                    HistoricTaskInstanceQuery sameAssigneeQuery = historyService.createHistoricTaskInstanceQuery()
                            .processInstanceId(task.getProcessInstanceId())
                            .taskAssignee(task.getAssignee()) // ç›¸åŒå®¡æ‰¹äºº
                            .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus())
                            .finished();
                    if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType())
                            && sameAssigneeQuery.count() > 0) {
                        getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                                .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName()));
                // éœ€è¦åŸºäºŽ instance è®¾ç½®ç§Ÿæˆ·ç¼–号,避免 Flowable å†…部异步执行时【例如:超时自动通过】 ä¸¢å¤±ç§Ÿæˆ·ç¼–号
                FlowableUtils.execute(processInstance.getTenantId(), () -> {
                    // è‡ªåŠ¨åŽ»é‡ï¼Œé€šè¿‡è‡ªåŠ¨å®¡æ‰¹çš„æ–¹å¼
                    BpmProcessDefinitionInfoDO processDefinitionInfo = bpmProcessDefinitionService.getProcessDefinitionInfo(task.getProcessDefinitionId());
                    if (processDefinitionInfo == null) {
                        log.error("[processTaskAssigned][taskId({}) æ²¡æœ‰æ‰¾åˆ°æµç¨‹å®šä¹‰({})]", task.getId(), task.getProcessDefinitionId());
                        return;
                    }
                    if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) {
                        BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
                        if (bpmnModel == null) {
                            log.error("[processTaskAssigned][taskId({}) æ²¡æœ‰æ‰¾åˆ°æµç¨‹æ¨¡åž‹({})]", task.getId(), task.getProcessDefinitionId());
                            return;
                        }
                        List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingFlows( // èŽ·å–æ‰€æœ‰ä¸Šä¸€ä¸ªèŠ‚ç‚¹
                                        BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())),
                                SequenceFlow::getSourceRef);
                        if (sameAssigneeQuery.taskDefinitionKeys(sourceTaskIds).count() > 0) {
                    if (processDefinitionInfo.getAutoApprovalType() != null) {
                        HistoricTaskInstanceQuery approvedTaskQuery = historyService.createHistoricTaskInstanceQuery()
                                .processInstanceId(task.getProcessInstanceId())
                                .taskVariableValueEquals(BpmnVariableConstants.TASK_VARIABLE_STATUS, BpmTaskStatusEnum.APPROVE.getStatus())
                                .finished();
                        if (BpmAutoApproveTypeEnum.APPROVE_ALL.getType().equals(processDefinitionInfo.getAutoApprovalType())
                                && approvedTaskQuery.taskAssignee(task.getAssignee()).count() > 0) {
                            getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                                    .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName()));
                                    .setReason(BpmAutoApproveTypeEnum.APPROVE_ALL.getName()));
                            return;
                        }
                    }
                }
                // èŽ·å–å‘èµ·äººèŠ‚ç‚¹
                BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
                if (bpmnModel == null) {
                    log.error("[processTaskAssigned][taskId({}) æ²¡æœ‰æ‰¾åˆ°æµç¨‹æ¨¡åž‹]", task.getId());
                    return;
                }
                FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
                // åˆ¤æ–­æ˜¯å¦ä¸ºé€€å›žæˆ–者驳回:如果是退回或者驳回不走这个策略(使用 local variable)
                Boolean returnTaskFlag = runtimeService.getVariableLocal(task.getExecutionId(),
                        String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
                Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(),
                        BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));
                if (userTaskElement.getId().equals(START_USER_NODE_ID)
                        && (skipStartUserNodeFlag == null // ç›®çš„:一般是“主流程”,发起人节点,自动通过审核
                        || BooleanUtil.isTrue(skipStartUserNodeFlag)) // ç›®çš„:一般是“子流程”,发起人节点,按配置自动通过审核
                        && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
                    getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                            .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
                    return;
                }
                // å½“不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum ç­–略进行处理
                if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID)
                        && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
                    if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
                        Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);
                        // æƒ…况一:自动跳过
                        if (ObjectUtils.equalsAny(assignStartUserHandlerType,
                                BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
                            getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                                    .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason()));
                            return;
                        }
                        // æƒ…况二:转交给部门负责人审批
                        if (ObjectUtils.equalsAny(assignStartUserHandlerType,
                                BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) {
                            AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
                            Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId());
                            DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null;
                            Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId());
                            // æ‰¾ä¸åˆ°éƒ¨é—¨è´Ÿè´£äººçš„æƒ…况下,自动审批通过
                            // noinspection DataFlowIssue
                            if (dept.getLeaderUserId() == null) {
                        // è¿žç»­å®¡æ‰¹çš„节点自动通过
                        if (BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getType().equals(processDefinitionInfo.getAutoApprovalType())) {
                            BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
                            if (bpmnModel == null) {
                                log.error("[processTaskAssigned][taskId({}) æ²¡æœ‰æ‰¾åˆ°æµç¨‹æ¨¡åž‹({})]", task.getId(), task.getProcessDefinitionId());
                                return;
                            }
                            List<String> sourceTaskIds = convertList(BpmnModelUtils.getElementIncomingUserTaskFlows( // èŽ·å–æ‰€æœ‰çš„ä¸Šä¸€ä¸ª UserTask èŠ‚ç‚¹è¿žçº¿
                                            BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey())),
                                    SequenceFlow::getSourceRef);
                            approvedTaskQuery.taskDefinitionKeys(sourceTaskIds).orderByTaskCreateTime().desc(); // è®¾ç½® taskIds, å¹¶æŒ‰åˆ›å»ºæ—¶é—´å€’序排序
                            HistoricTaskInstance firstHisTask = CollUtil.getFirst(approvedTaskQuery.list());
                            if (firstHisTask != null && StrUtil.equals(firstHisTask.getAssignee(), task.getAssignee())) {
                                getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                                        .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason()));
                                        .setReason(BpmAutoApproveTypeEnum.APPROVE_SEQUENT.getName()));
                                return;
                            }
                            // æ‰¾å¾—到部门负责人的情况下,修改负责人
                            if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {
                                getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()
                                        .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())
                                        .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason()));
                                return;
                            }
                            // å¦‚果部门负责人是自己,还是自己审批吧~
                        }
                    }
                }
                // æ³¨æ„ï¼šéœ€è¦åŸºäºŽ instance è®¾ç½®ç§Ÿæˆ·ç¼–号,避免 Flowable å†…部异步时,丢失租户编号
                FlowableUtils.execute(processInstance.getTenantId(), () -> {
                    // èŽ·å–å‘èµ·äººèŠ‚ç‚¹
                    BpmnModel bpmnModel = modelService.getBpmnModelByDefinitionId(processInstance.getProcessDefinitionId());
                    if (bpmnModel == null) {
                        log.error("[processTaskAssigned][taskId({}) æ²¡æœ‰æ‰¾åˆ°æµç¨‹æ¨¡åž‹]", task.getId());
                        return;
                    }
                    FlowElement userTaskElement = BpmnModelUtils.getFlowElementById(bpmnModel, task.getTaskDefinitionKey());
                    // åˆ¤æ–­æ˜¯å¦ä¸ºé€€å›žæˆ–者驳回:如果是退回或者驳回不走这个策略
                    Boolean returnTaskFlag = runtimeService.getVariable(processInstance.getProcessInstanceId(),
                            String.format(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_RETURN_FLAG, task.getTaskDefinitionKey()), Boolean.class);
                    Boolean skipStartUserNodeFlag = Convert.toBool(runtimeService.getVariable(processInstance.getProcessInstanceId(),
                            BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_SKIP_START_USER_NODE, String.class));
                    if (userTaskElement.getId().equals(START_USER_NODE_ID)
                            && (skipStartUserNodeFlag == null // ç›®çš„:一般是“主流程”,发起人节点,自动通过审核
                            || BooleanUtil.isTrue(skipStartUserNodeFlag)) // ç›®çš„:一般是“子流程”,发起人节点,按配置自动通过审核
                            && ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
                        getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                                .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP_START_USER_NODE.getReason()));
                        return;
                    }
                    // å½“不为发起人节点时,审批人与提交人为同一人时,根据 BpmUserTaskAssignStartUserHandlerTypeEnum ç­–略进行处理
                    if (ObjectUtil.notEqual(userTaskElement.getId(), START_USER_NODE_ID)
                            && StrUtil.equals(task.getAssignee(), processInstance.getStartUserId())) {
                        if (ObjUtil.notEqual(returnTaskFlag, Boolean.TRUE)) {
                            Integer assignStartUserHandlerType = BpmnModelUtils.parseAssignStartUserHandlerType(userTaskElement);
                            // æƒ…况一:自动跳过
                            if (ObjectUtils.equalsAny(assignStartUserHandlerType,
                                    BpmUserTaskAssignStartUserHandlerTypeEnum.SKIP.getType())) {
                                getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                                        .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_SKIP.getReason()));
                                return;
                            }
                            // æƒ…况二:转交给部门负责人审批
                            if (ObjectUtils.equalsAny(assignStartUserHandlerType,
                                    BpmUserTaskAssignStartUserHandlerTypeEnum.TRANSFER_DEPT_LEADER.getType())) {
                                AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
                                Assert.notNull(startUser, "提交人({})信息为空", processInstance.getStartUserId());
                                DeptRespDTO dept = startUser.getDeptId() != null ? deptApi.getDept(startUser.getDeptId()) : null;
                                Assert.notNull(dept, "提交人({})部门({})信息为空", processInstance.getStartUserId(), startUser.getDeptId());
                                // æ‰¾ä¸åˆ°éƒ¨é—¨è´Ÿè´£äººçš„æƒ…况下,自动审批通过
                                // noinspection DataFlowIssue
                                if (dept.getLeaderUserId() == null) {
                                    getSelf().approveTask(Long.valueOf(task.getAssignee()), new BpmTaskApproveReqVO().setId(task.getId())
                                            .setReason(BpmReasonEnum.ASSIGN_START_USER_APPROVE_WHEN_DEPT_LEADER_NOT_FOUND.getReason()));
                                    return;
                                }
                                // æ‰¾å¾—到部门负责人的情况下,修改负责人
                                if (ObjectUtil.notEqual(dept.getLeaderUserId(), startUser.getId())) {
                                    getSelf().transferTask(Long.valueOf(task.getAssignee()), new BpmTaskTransferReqVO()
                                            .setId(task.getId()).setAssigneeUserId(dept.getLeaderUserId())
                                            .setReason(BpmReasonEnum.ASSIGN_START_USER_TRANSFER_DEPT_LEADER.getReason()));
                                    return;
                                }
                                // å¦‚果部门负责人是自己,还是自己审批吧~
                            }
                        }
                    }
                    // å‘送消息
                    AdminUserRespDTO startUser = adminUserApi.getUser(Long.valueOf(processInstance.getStartUserId()));
                    messageService.sendMessageWhenTaskAssigned(BpmTaskConvert.INSTANCE.convert(processInstance, startUser, task));
                });
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/BpmTaskCandidateInvokerTest.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.extra.spring.SpringUtil;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
@@ -64,7 +65,7 @@
    public void setUp() {
        userStrategy = new BpmTaskCandidateUserStrategy(); // åˆ›å»º strategy å®žä¾‹
        when(emptyStrategy.getStrategy()).thenReturn(BpmTaskCandidateStrategyEnum.ASSIGN_EMPTY);
        strategyList = List.of(userStrategy, emptyStrategy); // åˆ›å»º strategyList
        strategyList = ListUtil.of(userStrategy, emptyStrategy); // åˆ›å»º strategyList
        taskCandidateInvoker = new BpmTaskCandidateInvoker(strategyList, adminUserApi);
    }
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/candidate/strategy/dept/BpmTaskCandidateStartUserSelectStrategyTest.java
@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.bpm.framework.flowable.core.candidate.strategy.dept;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.map.MapUtil;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.bpm.framework.flowable.core.enums.BpmnVariableConstants;
@@ -12,7 +13,6 @@
import org.mockito.Mock;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
@@ -41,7 +41,7 @@
        // mock æ–¹æ³•(FlowableUtils)
        Map<String, Object> processVariables = new HashMap<>();
        processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
                MapUtil.of("activity_001", List.of(1L, 2L)));
                MapUtil.of("activity_001", ListUtil.of(1L, 2L)));
        when(processInstance.getProcessVariables()).thenReturn(processVariables);
        // è°ƒç”¨
@@ -56,7 +56,7 @@
        String activityId = "activity_001";
        Map<String, Object> processVariables = new HashMap<>();
        processVariables.put(BpmnVariableConstants.PROCESS_INSTANCE_VARIABLE_START_USER_SELECT_ASSIGNEES,
                MapUtil.of("activity_001", List.of(1L, 2L)));
                MapUtil.of("activity_001", ListUtil.of(1L, 2L)));
        // è°ƒç”¨
        Set<Long> userIds = strategy.calculateUsersByActivity(null, activityId, null,
yudao-module-bpm/src/test/java/cn/iocoder/yudao/module/bpm/framework/flowable/core/util/FlowableUtilsTest.java
¶Ô±ÈÐÂÎļþ
@@ -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\"}";
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/business/CrmBusinessController.java
@@ -138,6 +138,26 @@
                        .setCustomerId(business.getCustomerId())));
    }
    @GetMapping("/list-by-customer")
    @Operation(summary = "获得商机列表,基于指定客户")
    @Parameter(name = "customerId", description = "客户编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:business:query')")
    public CommonResult<List<CrmBusinessRespVO>> getBusinessListByCustomer(@RequestParam("customerId") Long customerId) {
        List<CrmBusinessDO> list = businessService.getBusinessListByCustomerId(customerId);
        return success(convertList(list, business -> // åªè¿”回 id、name å­—段
                new CrmBusinessRespVO().setId(business.getId()).setName(business.getName())
                        .setCustomerId(business.getCustomerId())));
    }
    @GetMapping("/list-by-contact")
    @Operation(summary = "获得商机列表,基于指定联系人")
    @Parameter(name = "contactId", description = "联系人编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:business:query')")
    public CommonResult<List<CrmBusinessRespVO>> getBusinessListByContact(@RequestParam("contactId") Long contactId) {
        List<CrmBusinessDO> list = businessService.getBusinessListByContact(contactId);
        return success(buildBusinessDetailList(list));
    }
    @GetMapping("/page")
    @Operation(summary = "获得商机分页")
    @PreAuthorize("@ss.hasPermission('crm:business:query')")
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/contact/CrmContactController.java
@@ -114,6 +114,17 @@
                        .setCustomerId(contact.getCustomerId())));
    }
    @GetMapping("/list-by-customer")
    @Operation(summary = "获得联系人列表,基于指定客户")
    @Parameter(name = "customerId", description = "客户编号", required = true)
    @PreAuthorize("@ss.hasPermission('crm:contact:query')")
    public CommonResult<List<CrmContactRespVO>> getContactListByCustomer(@RequestParam("customerId") Long customerId) {
        List<CrmContactDO> list = contactService.getContactListByCustomerId(customerId);
        return success(convertList(list, contact -> // åªè¿”回 id、name å­—段
                new CrmContactRespVO().setId(contact.getId()).setName(contact.getName())
                        .setCustomerId(contact.getCustomerId())));
    }
    @GetMapping("/page")
    @Operation(summary = "获得联系人分页")
    @PreAuthorize("@ss.hasPermission('crm:contact:query')")
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/controller/admin/followup/CrmFollowUpRecordController.java
@@ -11,9 +11,12 @@
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.followup.CrmFollowUpRecordDO;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
import cn.iocoder.yudao.module.crm.enums.permission.CrmPermissionLevelEnum;
import cn.iocoder.yudao.module.crm.service.business.CrmBusinessService;
import cn.iocoder.yudao.module.crm.service.contact.CrmContactService;
import cn.iocoder.yudao.module.crm.service.followup.CrmFollowUpRecordService;
import cn.iocoder.yudao.module.crm.service.permission.CrmPermissionService;
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;
@@ -27,10 +30,13 @@
import java.util.ArrayList;
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.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.CRM_PERMISSION_DENIED;
import static cn.iocoder.yudao.module.crm.enums.ErrorCodeConstants.FOLLOW_UP_RECORD_NOT_EXISTS;
@Tag(name = "管理后台 - è·Ÿè¿›è®°å½•")
@@ -45,6 +51,8 @@
    private CrmContactService contactService;
    @Resource
    private CrmBusinessService businessService;
    @Resource
    private CrmPermissionService permissionService;
    @Resource
    private AdminUserApi adminUserApi;
@@ -68,6 +76,13 @@
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    public CommonResult<CrmFollowUpRecordRespVO> getFollowUpRecord(@RequestParam("id") Long id) {
        CrmFollowUpRecordDO followUpRecord = followUpRecordService.getFollowUpRecord(id);
        if (followUpRecord == null) {
            throw exception(FOLLOW_UP_RECORD_NOT_EXISTS);
        }
        if (!permissionService.hasPermission(followUpRecord.getBizType(), followUpRecord.getBizId(),
                getLoginUserId(), CrmPermissionLevelEnum.READ)) {
            throw exception(CRM_PERMISSION_DENIED, CrmBizTypeEnum.getNameByType(followUpRecord.getBizType()));
        }
        return success(BeanUtils.toBean(followUpRecord, CrmFollowUpRecordRespVO.class));
    }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/business/CrmBusinessMapper.java
@@ -65,6 +65,10 @@
                .eq(CrmBusinessDO::getOwnerUserId, ownerUserId));
    }
    default List<CrmBusinessDO> selectListByCustomerId(Long customerId) {
        return selectList(CrmBusinessDO::getCustomerId, customerId);
    }
    default PageResult<CrmBusinessDO> selectPage(CrmStatisticsFunnelReqVO pageVO) {
        return selectPage(pageVO, new LambdaQueryWrapperX<CrmBusinessDO>()
                .in(CrmBusinessDO::getOwnerUserId, pageVO.getUserIds())
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/dal/mysql/customer/CrmCustomerLimitConfigMapper.java
@@ -3,6 +3,7 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
import cn.iocoder.yudao.module.crm.controller.admin.customer.vo.limitconfig.CrmCustomerLimitConfigPageReqVO;
import cn.iocoder.yudao.module.crm.dal.dataobject.customer.CrmCustomerLimitConfigDO;
import org.apache.ibatis.annotations.Mapper;
@@ -28,9 +29,9 @@
        LambdaQueryWrapperX<CrmCustomerLimitConfigDO> query = new LambdaQueryWrapperX<CrmCustomerLimitConfigDO>()
                .eq(CrmCustomerLimitConfigDO::getType, type);
        query.and(w -> {
            w.apply("FIND_IN_SET({0}, user_ids) > 0", userId);
            w.apply(MyBatisUtils.findInSet("user_ids"), userId);
            if (deptId != null) {
                w.or().apply("FIND_IN_SET({0}, dept_ids) > 0", deptId);
                w.or().apply(MyBatisUtils.findInSet("dept_ids"), deptId);
            }
        });
        return selectList(query);
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/enums/ErrorCodeConstants.java
@@ -26,6 +26,7 @@
    ErrorCode BUSINESS_DELETE_FAIL_CONTRACT_EXISTS = new ErrorCode(1_020_002_001, "商机已关联合同,不能删除");
    ErrorCode BUSINESS_UPDATE_STATUS_FAIL_END_STATUS = new ErrorCode(1_020_002_002, "更新商机状态失败,原因:已经是结束状态");
    ErrorCode BUSINESS_UPDATE_STATUS_FAIL_STATUS_EQUALS = new ErrorCode(1_020_002_003, "更新商机状态失败,原因:已经是该状态");
    ErrorCode BUSINESS_CONTACT_CUSTOMER_NOT_MATCH = new ErrorCode(1_020_002_004, "商机关联的联系人不属于该客户");
    // ========== è”系人管理 1-020-003-000 ==========
    ErrorCode CONTACT_NOT_EXISTS = new ErrorCode(1_020_003_000, "联系人不存在");
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessService.java
@@ -196,6 +196,22 @@
    List<CrmBusinessDO> getBusinessListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
    /**
     * èŽ·å¾—å•†æœºåˆ—è¡¨ï¼ŒåŸºäºŽæŒ‡å®šå®¢æˆ·
     *
     * @param customerId å®¢æˆ·ç¼–号
     * @return å•†æœºåˆ—表
     */
    List<CrmBusinessDO> getBusinessListByCustomerId(Long customerId);
    /**
     * èŽ·å¾—å•†æœºåˆ—è¡¨ï¼ŒåŸºäºŽæŒ‡å®šè”ç³»äºº
     *
     * @param contactId è”系人编号
     * @return å•†æœºåˆ—表
     */
    List<CrmBusinessDO> getBusinessListByContact(Long contactId);
    /**
     * èŽ·å¾—å•†æœºåˆ†é¡µï¼Œç›®å‰ç”¨äºŽã€æ•°æ®ç»Ÿè®¡ã€‘
     *
     * @param pageVO è¯·æ±‚
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/business/CrmBusinessServiceImpl.java
@@ -2,6 +2,7 @@
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
@@ -15,6 +16,7 @@
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessProductDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.business.CrmBusinessStatusDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactBusinessDO;
import cn.iocoder.yudao.module.crm.dal.dataobject.contact.CrmContactDO;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessMapper;
import cn.iocoder.yudao.module.crm.dal.mysql.business.CrmBusinessProductMapper;
import cn.iocoder.yudao.module.crm.enums.common.CrmBizTypeEnum;
@@ -194,9 +196,15 @@
        if (saveReqVO.getCustomerId() != null) {
            customerService.validateCustomer(saveReqVO.getCustomerId());
        }
        // æ ¡éªŒè”系人
        // æ ¡éªŒè”系人(且必须与商机属于同一客户,避免跨客户关联)
        if (saveReqVO.getContactId() != null) {
            contactService.validateContact(saveReqVO.getContactId());
            if (saveReqVO.getCustomerId() != null) {
                CrmContactDO contact = contactService.getContact(saveReqVO.getContactId());
                if (ObjUtil.notEqual(saveReqVO.getCustomerId(), contact.getCustomerId())) {
                    throw exception(BUSINESS_CONTACT_CUSTOMER_NOT_MATCH);
                }
            }
        }
        // æ ¡éªŒè´Ÿè´£äºº
        if (saveReqVO.getOwnerUserId() != null) {
@@ -378,6 +386,24 @@
    }
    @Override
    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#customerId", level = CrmPermissionLevelEnum.READ)
    public List<CrmBusinessDO> getBusinessListByCustomerId(Long customerId) {
        return businessMapper.selectListByCustomerId(customerId);
    }
    @Override
    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CONTACT, bizId = "#contactId", level = CrmPermissionLevelEnum.READ)
    public List<CrmBusinessDO> getBusinessListByContact(Long contactId) {
        // 1. æŸ¥è¯¢å…³è”的商机编号
        List<CrmContactBusinessDO> contactBusinessList = contactBusinessService.getContactBusinessListByContactId(contactId);
        if (CollUtil.isEmpty(contactBusinessList)) {
            return ListUtil.empty();
        }
        // 2. æŸ¥è¯¢å•†æœºåˆ—表
        return businessMapper.selectByIds(convertSet(contactBusinessList, CrmContactBusinessDO::getBusinessId));
    }
    @Override
    public PageResult<CrmBusinessDO> getBusinessPageByDate(CrmStatisticsFunnelReqVO pageVO) {
        return businessMapper.selectPage(pageVO);
    }
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactService.java
@@ -169,4 +169,12 @@
     */
    List<CrmContactDO> getContactListByCustomerIdOwnerUserId(Long customerId, Long ownerUserId);
    /**
     * èŽ·å¾—è”ç³»äººåˆ—è¡¨ï¼ŒåŸºäºŽæŒ‡å®šå®¢æˆ·
     *
     * @param customerId å®¢æˆ·ç¼–号
     * @return è”系人列表
     */
    List<CrmContactDO> getContactListByCustomerId(Long customerId);
}
yudao-module-crm/src/main/java/cn/iocoder/yudao/module/crm/service/contact/CrmContactServiceImpl.java
@@ -304,4 +304,10 @@
        return contactMapper.selectListByCustomerIdOwnerUserId(customerId, ownerUserId);
    }
    @Override
    @CrmPermission(bizType = CrmBizTypeEnum.CRM_CUSTOMER, bizId = "#customerId", level = CrmPermissionLevelEnum.READ)
    public List<CrmContactDO> getContactListByCustomerId(Long customerId) {
        return contactMapper.selectListByCustomerId(customerId);
    }
}
yudao-module-erp/pom.xml
@@ -24,27 +24,6 @@
            <version>${revision}</version>
        </dependency>
        <!-- MDM ä¸»æ•°æ®ç®¡ç†æ¨¡å— API -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-module-mdm</artifactId>
            <version>${revision}</version>
        </dependency>
        <!-- WMS ä»“库管理模块 -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-module-wms</artifactId>
            <version>${revision}</version>
        </dependency>
        <!-- BPM æµç¨‹æ¨¡å— -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-module-bpm</artifactId>
            <version>${revision}</version>
        </dependency>
        <!-- ä¸šåŠ¡ç»„ä»¶ -->
        <!-- Web ç›¸å…³ -->
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/product/vo/product/ErpProductPageReqVO.java
@@ -20,9 +20,6 @@
    @Schema(description = "产品分类编号", example = "11161")
    private Long categoryId;
    @Schema(description = "产品状态", example = "0")
    private Integer status;
    @Schema(description = "创建时间")
    @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
    private LocalDateTime[] createTime;
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseInController.java
@@ -41,7 +41,6 @@
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - ERP é‡‡è´­å…¥åº“")
@RestController
@@ -82,15 +81,6 @@
    public CommonResult<Boolean> updatePurchaseInStatus(@RequestParam("id") Long id,
                                                      @RequestParam("status") Integer status) {
        purchaseInService.updatePurchaseInStatus(id, status);
        return success(true);
    }
    @PostMapping("/submit")
    @Operation(summary = "提交采购入库审批")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('erp:purchase-in:update')")
    public CommonResult<Boolean> submitPurchaseIn(@RequestParam("id") Long id) {
        purchaseInService.submitPurchaseIn(id, getLoginUserId());
        return success(true);
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/purchase/ErpPurchaseOrderController.java
@@ -40,7 +40,6 @@
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - ERP é‡‡è´­è®¢å•")
@RestController
@@ -81,15 +80,6 @@
    public CommonResult<Boolean> updatePurchaseOrderStatus(@RequestParam("id") Long id,
                                                      @RequestParam("status") Integer status) {
        purchaseOrderService.updatePurchaseOrderStatus(id, status);
        return success(true);
    }
    @PostMapping("/submit")
    @Operation(summary = "提交采购订单审批")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('erp:purchase-order:update')")
    public CommonResult<Boolean> submitPurchaseOrder(@RequestParam("id") Long id) {
        purchaseOrderService.submitPurchaseOrder(id, getLoginUserId());
        return success(true);
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOrderController.java
@@ -40,7 +40,6 @@
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - ERP é”€å”®è®¢å•")
@RestController
@@ -62,14 +61,14 @@
    @PostMapping("/create")
    @Operation(summary = "创建销售订单")
    @PreAuthorize("@ss.hasPermission('erp:sale-out:create')")
    @PreAuthorize("@ss.hasPermission('erp:sale-order:create')")
    public CommonResult<Long> createSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO createReqVO) {
        return success(saleOrderService.createSaleOrder(createReqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "更新销售订单")
    @PreAuthorize("@ss.hasPermission('erp:sale-out:update')")
    @PreAuthorize("@ss.hasPermission('erp:sale-order:update')")
    public CommonResult<Boolean> updateSaleOrder(@Valid @RequestBody ErpSaleOrderSaveReqVO updateReqVO) {
        saleOrderService.updateSaleOrder(updateReqVO);
        return success(true);
@@ -77,26 +76,17 @@
    @PutMapping("/update-status")
    @Operation(summary = "更新销售订单的状态")
    @PreAuthorize("@ss.hasPermission('erp:sale-out:update-status')")
    @PreAuthorize("@ss.hasPermission('erp:sale-order:update-status')")
    public CommonResult<Boolean> updateSaleOrderStatus(@RequestParam("id") Long id,
                                                      @RequestParam("status") Integer status) {
        saleOrderService.updateSaleOrderStatus(id, status);
        return success(true);
    }
    @PostMapping("/submit")
    @Operation(summary = "提交销售订单审批")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('erp:sale-out:update')")
    public CommonResult<Boolean> submitSaleOrder(@RequestParam("id") Long id) {
        saleOrderService.submitSaleOrder(id, getLoginUserId());
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除销售订单")
    @Parameter(name = "ids", description = "编号数组", required = true)
    @PreAuthorize("@ss.hasPermission('erp:sale-out:delete')")
    @PreAuthorize("@ss.hasPermission('erp:sale-order:delete')")
    public CommonResult<Boolean> deleteSaleOrder(@RequestParam("ids") List<Long> ids) {
        saleOrderService.deleteSaleOrder(ids);
        return success(true);
@@ -105,7 +95,7 @@
    @GetMapping("/get")
    @Operation(summary = "获得销售订单")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('erp:sale-out:query')")
    @PreAuthorize("@ss.hasPermission('erp:sale-order:query')")
    public CommonResult<ErpSaleOrderRespVO> getSaleOrder(@RequestParam("id") Long id) {
        ErpSaleOrderDO saleOrder = saleOrderService.getSaleOrder(id);
        if (saleOrder == null) {
@@ -125,7 +115,7 @@
    @GetMapping("/page")
    @Operation(summary = "获得销售订单分页")
    @PreAuthorize("@ss.hasPermission('erp:sale-out:query')")
    @PreAuthorize("@ss.hasPermission('erp:sale-order:query')")
    public CommonResult<PageResult<ErpSaleOrderRespVO>> getSaleOrderPage(@Valid ErpSaleOrderPageReqVO pageReqVO) {
        PageResult<ErpSaleOrderDO> pageResult = saleOrderService.getSaleOrderPage(pageReqVO);
        return success(buildSaleOrderVOPageResult(pageResult));
@@ -133,7 +123,7 @@
    @GetMapping("/export-excel")
    @Operation(summary = "导出销售订单 Excel")
    @PreAuthorize("@ss.hasPermission('erp:sale-out:export')")
    @PreAuthorize("@ss.hasPermission('erp:sale-order:export')")
    @ApiAccessLog(operateType = EXPORT)
    public void exportSaleOrderExcel(@Valid ErpSaleOrderPageReqVO pageReqVO,
                                    HttpServletResponse response) throws IOException {
@@ -171,4 +161,4 @@
        });
    }
}
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/controller/admin/sale/ErpSaleOutController.java
@@ -41,7 +41,6 @@
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMultiMap;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - ERP é”€å”®å‡ºåº“")
@RestController
@@ -82,15 +81,6 @@
    public CommonResult<Boolean> updateSaleOutStatus(@RequestParam("id") Long id,
                                                      @RequestParam("status") Integer status) {
        saleOutService.updateSaleOutStatus(id, status);
        return success(true);
    }
    @PostMapping("/submit")
    @Operation(summary = "提交销售出库审批")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('erp:sale-out:update')")
    public CommonResult<Boolean> submitSaleOut(@RequestParam("id") Long id) {
        saleOutService.submitSaleOut(id, getLoginUserId());
        return success(true);
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseInDO.java
@@ -119,10 +119,4 @@
     */
    private String remark;
    // ========== BPM æµç¨‹å®¡æ‰¹ ==========
    /**
     * BPM æµç¨‹å®žä¾‹ç¼–号
     */
    private String processInstanceId;
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/purchase/ErpPurchaseOrderDO.java
@@ -100,12 +100,6 @@
     */
    private String remark;
    // ========== BPM æµç¨‹å®¡æ‰¹ ==========
    /**
     * BPM æµç¨‹å®žä¾‹ç¼–号
     */
    private String processInstanceId;
    // ========== é‡‡è´­å…¥åº“ ==========
    /**
     * é‡‡è´­å…¥åº“数量
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOrderDO.java
@@ -106,12 +106,6 @@
     */
    private String remark;
    // ========== BPM æµç¨‹å®¡æ‰¹ ==========
    /**
     * BPM æµç¨‹å®žä¾‹ç¼–号
     */
    private String processInstanceId;
    // ========== é”€å”®å‡ºåº“ ==========
    /**
     * é”€å”®å‡ºåº“数量
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/dal/dataobject/sale/ErpSaleOutDO.java
@@ -125,10 +125,4 @@
     */
    private String remark;
    // ========== BPM æµç¨‹å®¡æ‰¹ ==========
    /**
     * BPM æµç¨‹å®žä¾‹ç¼–号
     */
    private String processInstanceId;
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErpAuditStatus.java
@@ -17,10 +17,8 @@
@Getter
public enum ErpAuditStatus implements ArrayValuable<Integer> {
    DRAFT(0, "草稿"), // è‰ç¨¿çŠ¶æ€ï¼Œæœªæäº¤å®¡æ‰¹
    PROCESS(10, "审批中"), // å®¡æ‰¹ä¸­
    APPROVE(20, "审批通过"), // å®¡æ‰¹é€šè¿‡
    REJECT(30, "审批不通过"); // å®¡æ‰¹ä¸é€šè¿‡
    PROCESS(10, "未审核"), // å®¡æ ¸ä¸­
    APPROVE(20, "已审核"); // å®¡æ ¸é€šè¿‡
    public static final Integer[] ARRAYS = Arrays.stream(values()).map(ErpAuditStatus::getStatus).toArray(Integer[]::new);
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/enums/ErrorCodeConstants.java
@@ -25,8 +25,6 @@
    ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_IN = new ErrorCode(1_030_101_008, "反审核失败,已存在对应的采购入库单");
ErrorCode PURCHASE_ORDER_ITEM_RETURN_FAIL_IN_EXCEED = new ErrorCode(1_030_101_009, "采购订单项({})超过最大允许退货数量({})");
    ErrorCode PURCHASE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_030_101_010, "反审核失败,已存在对应的采购退货单");
    ErrorCode PURCHASE_ORDER_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_030_101_011, "提交审批失败,只有草稿状态的采购订单才能提交审批");
    ErrorCode PURCHASE_ORDER_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_030_101_012, "更新审批状态失败,只有审批中的采购订单才能更新审批状态");
    // ========== ERP é‡‡è´­å…¥åº“(1-030-102-000) ==========
    ErrorCode PURCHASE_IN_NOT_EXISTS = new ErrorCode(1_030_102_000, "采购入库单不存在");
@@ -38,8 +36,6 @@
    ErrorCode PURCHASE_IN_NOT_APPROVE = new ErrorCode(1_030_102_006, "采购入库单未审核,无法操作");
    ErrorCode PURCHASE_IN_FAIL_PAYMENT_PRICE_EXCEED = new ErrorCode(1_030_102_007, "付款金额({})超过采购入库单总金额({})");
    ErrorCode PURCHASE_IN_PROCESS_FAIL_EXISTS_PAYMENT = new ErrorCode(1_030_102_008, "反审核失败,已存在对应的付款单");
    ErrorCode PURCHASE_IN_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_030_102_009, "提交审批失败,只有草稿状态的采购入库单才能提交审批");
    ErrorCode PURCHASE_IN_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_030_102_010, "更新审批状态失败,只有审批中的采购入库单才能更新审批状态");
    // ========== ERP é‡‡è´­é€€è´§ï¼ˆ1-030-103-000) ==========
    ErrorCode PURCHASE_RETURN_NOT_EXISTS = new ErrorCode(1_030_103_000, "采购退货单不存在");
@@ -68,8 +64,6 @@
    ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_OUT = new ErrorCode(1_020_201_008, "反审核失败,已存在对应的销售出库单");
    ErrorCode SALE_ORDER_ITEM_RETURN_FAIL_OUT_EXCEED = new ErrorCode(1_020_201_009, "销售订单项({})超过最大允许退货数量({})");
    ErrorCode SALE_ORDER_PROCESS_FAIL_EXISTS_RETURN = new ErrorCode(1_020_201_010, "反审核失败,已存在对应的销售退货单");
    ErrorCode SALE_ORDER_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_201_011, "提交审批失败,只有草稿状态的销售订单才能提交审批");
    ErrorCode SALE_ORDER_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_201_012, "更新审批状态失败,只有审批中的销售订单才能更新审批状态");
    // ========== ERP é”€å”®å‡ºåº“(1-030-202-000) ==========
    ErrorCode SALE_OUT_NOT_EXISTS = new ErrorCode(1_020_202_000, "销售出库单不存在");
@@ -81,8 +75,6 @@
    ErrorCode SALE_OUT_NOT_APPROVE = new ErrorCode(1_020_202_006, "销售出库单未审核,无法操作");
    ErrorCode SALE_OUT_FAIL_RECEIPT_PRICE_EXCEED = new ErrorCode(1_020_202_007, "收款金额({})超过销售出库单总金额({})");
    ErrorCode SALE_OUT_PROCESS_FAIL_EXISTS_RECEIPT = new ErrorCode(1_020_202_008, "反审核失败,已存在对应的收款单");
    ErrorCode SALE_OUT_SUBMIT_FAIL_NOT_DRAFT = new ErrorCode(1_020_202_009, "提交审批失败,只有草稿状态的销售出库单才能提交审批");
    ErrorCode SALE_OUT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS = new ErrorCode(1_020_202_010, "更新审批状态失败,只有审批中的销售出库单才能更新审批状态");
    // ========== ERP é”€å”®é€€è´§ï¼ˆ1-030-203-000) ==========
    ErrorCode SALE_RETURN_NOT_EXISTS = new ErrorCode(1_020_203_000, "销售退货单不存在");
@@ -138,7 +130,6 @@
    // ========== ERP äº§å“ 1-030-500-000 ==========
    ErrorCode PRODUCT_NOT_EXISTS = new ErrorCode(1_030_500_000, "产品不存在");
    ErrorCode PRODUCT_NOT_ENABLE = new ErrorCode(1_030_500_001, "产品({})未启用");
    ErrorCode PRODUCT_BAR_CODE_EXISTS = new ErrorCode(1_030_500_002, "产品条码已存在");
    // ========== ERP äº§å“åˆ†ç±» 1-030-501-000 ==========
    ErrorCode PRODUCT_CATEGORY_NOT_EXISTS = new ErrorCode(1_030_501_000, "产品分类不存在");
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/product/ErpProductServiceImpl.java
@@ -8,15 +8,10 @@
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ErpProductRespVO;
import cn.iocoder.yudao.module.erp.controller.admin.product.vo.product.ProductSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductCategoryDO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemCreateReqDTO;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemRespDTO;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemUpdateReqDTO;
import cn.iocoder.yudao.module.mdm.api.category.MdmItemCategoryApi;
import cn.iocoder.yudao.module.mdm.api.category.dto.MdmItemCategoryRespDTO;
import cn.iocoder.yudao.module.mdm.api.unit.MdmUnitMeasureApi;
import cn.iocoder.yudao.module.mdm.api.unit.dto.MdmUnitMeasureRespDTO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductUnitDO;
import cn.iocoder.yudao.module.erp.dal.mysql.product.ErpProductMapper;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
@@ -30,57 +25,46 @@
/**
 * ERP äº§å“ Service å®žçŽ°ç±»
 *
 * æ”¹é€ è¯´æ˜Žï¼šERP äº§å“æ•°æ®æ¥æºäºŽ MDM ç‰©æ–™ä¸»æ•°æ®
 * - æ‰€æœ‰äº§å“æ“ä½œå‡è°ƒç”¨ MDM API
 * - ERP äº§å“å¯¹åº” MDM ç‰©æ–™ç±»åž‹ = æˆå“ï¼ˆitemType = 2)
 *
 * @author èŠ‹é“æºç 
 */
@Service
@Validated
public class ErpProductServiceImpl implements ErpProductService {
    /** ERP äº§å“å¯¹åº”çš„ MDM ç‰©æ–™ç±»åž‹ï¼šæˆå“ */
    private static final int MDM_ITEM_TYPE_PRODUCT = 2;
    @Resource
    private ErpProductMapper productMapper;
    @Resource
    private MdmItemApi mdmItemApi;
    private ErpProductCategoryService productCategoryService;
    @Resource
    private MdmItemCategoryApi mdmItemCategoryApi;
    @Resource
    private MdmUnitMeasureApi mdmUnitMeasureApi;
    private ErpProductUnitService productUnitService;
    @Override
    public Long createProduct(ProductSaveReqVO createReqVO) {
        // 1. æ ¡éªŒæ¡ç å”¯ä¸€
        validateProductBarCodeUnique(null, createReqVO.getBarCode());
        // 2. æž„建创建请求并调用 MDM API
        MdmItemCreateReqDTO createReqDTO = buildCreateReqDTO(createReqVO);
        return mdmItemApi.createItem(createReqDTO).getCheckedData();
        // TODO èŠ‹è‰¿ï¼šæ ¡éªŒåˆ†ç±»
        // æ’å…¥
        ErpProductDO product = BeanUtils.toBean(createReqVO, ErpProductDO.class);
        productMapper.insert(product);
        // è¿”回
        return product.getId();
    }
    @Override
    public void updateProduct(ProductSaveReqVO updateReqVO) {
        // 1. æ ¡éªŒå­˜åœ¨
        // TODO èŠ‹è‰¿ï¼šæ ¡éªŒåˆ†ç±»
        // æ ¡éªŒå­˜åœ¨
        validateProductExists(updateReqVO.getId());
        // 2. æ ¡éªŒæ¡ç å”¯ä¸€
        validateProductBarCodeUnique(updateReqVO.getId(), updateReqVO.getBarCode());
        // 3. æž„建更新请求并调用 MDM API
        MdmItemUpdateReqDTO updateReqDTO = buildUpdateReqDTO(updateReqVO);
        mdmItemApi.updateItem(updateReqDTO);
        // æ›´æ–°
        ErpProductDO updateObj = BeanUtils.toBean(updateReqVO, ErpProductDO.class);
        productMapper.updateById(updateObj);
    }
    @Override
    public void deleteProduct(Long id) {
        // æ ¡éªŒå­˜åœ¨
        validateProductExists(id);
        // ERP äº§å“åˆ é™¤å®žé™…为禁用(MDM ç‰©æ–™ä¸èƒ½ç‰©ç†åˆ é™¤ï¼‰
        mdmItemApi.updateItemStatus(id, CommonStatusEnum.DISABLE.getStatus());
        // åˆ é™¤
        productMapper.deleteById(id);
    }
    @Override
@@ -88,36 +72,35 @@
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        // 1. è°ƒç”¨ MDM API èŽ·å–ç‰©æ–™åˆ—è¡¨
        List<MdmItemRespDTO> itemList = mdmItemApi.getItemList(ids).getCheckedData();
        Map<Long, MdmItemRespDTO> itemMap = convertMap(itemList, MdmItemRespDTO::getId);
        // 2. æ ¡éªŒæ¯ä¸ªäº§å“
        List<ErpProductDO> list = productMapper.selectByIds(ids);
        Map<Long, ErpProductDO> productMap = convertMap(list, ErpProductDO::getId);
        for (Long id : ids) {
            MdmItemRespDTO item = itemMap.get(id);
            if (item == null) {
            ErpProductDO product = productMap.get(id);
            if (productMap.get(id) == null) {
                throw exception(PRODUCT_NOT_EXISTS);
            }
            if (CommonStatusEnum.isDisable(item.getStatus())) {
                throw exception(PRODUCT_NOT_ENABLE, item.getName());
            if (CommonStatusEnum.isDisable(product.getStatus())) {
                throw exception(PRODUCT_NOT_ENABLE, product.getName());
            }
        }
        return list;
    }
        // 3. è½¬æ¢ä¸º ERP Product DO(手动映射字段)
        return convertToErpProductDOList(itemList);
    private void validateProductExists(Long id) {
        if (productMapper.selectById(id) == null) {
            throw exception(PRODUCT_NOT_EXISTS);
        }
    }
    @Override
    public ErpProductDO getProduct(Long id) {
        MdmItemRespDTO item = mdmItemApi.getItem(id).getCheckedData();
        return convertToErpProductDO(item);
        return productMapper.selectById(id);
    }
    @Override
    public List<ErpProductRespVO> getProductVOListByStatus(Integer status) {
        List<MdmItemRespDTO> itemList = mdmItemApi.getItemListByStatus(status).getCheckedData();
        return buildProductVOList(itemList);
        List<ErpProductDO> list = productMapper.selectListByStatus(status);
        return buildProductVOList(list);
    }
    @Override
@@ -125,188 +108,40 @@
        if (CollUtil.isEmpty(ids)) {
            return Collections.emptyList();
        }
        List<MdmItemRespDTO> itemList = mdmItemApi.getItemList(ids).getCheckedData();
        return buildProductVOList(itemList);
        List<ErpProductDO> list = productMapper.selectByIds(ids);
        return buildProductVOList(list);
    }
    @Override
    public PageResult<ErpProductRespVO> getProductVOPage(ErpProductPageReqVO pageReqVO) {
        // è°ƒç”¨ MDM API åˆ†é¡µæŸ¥è¯¢
        PageResult<MdmItemRespDTO> mdmPageResult = mdmItemApi.getItemPage(
                MDM_ITEM_TYPE_PRODUCT,
                pageReqVO.getStatus(),
                pageReqVO.getPageNo(),
                pageReqVO.getPageSize()
        ).getCheckedData();
        PageResult<ErpProductDO> pageResult = productMapper.selectPage(pageReqVO);
        return new PageResult<>(buildProductVOList(pageResult.getList()), pageResult.getTotal());
    }
        List<ErpProductRespVO> productVOList = buildProductVOList(mdmPageResult.getList());
        return new PageResult<>(productVOList, mdmPageResult.getTotal());
    private List<ErpProductRespVO> buildProductVOList(List<ErpProductDO> list) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        Map<Long, ErpProductCategoryDO> categoryMap = productCategoryService.getProductCategoryMap(
                convertSet(list, ErpProductDO::getCategoryId));
        Map<Long, ErpProductUnitDO> unitMap = productUnitService.getProductUnitMap(
                convertSet(list, ErpProductDO::getUnitId));
        return BeanUtils.toBean(list, ErpProductRespVO.class, product -> {
            MapUtils.findAndThen(categoryMap, product.getCategoryId(),
                    category -> product.setCategoryName(category.getName()));
            MapUtils.findAndThen(unitMap, product.getUnitId(),
                    unit -> product.setUnitName(unit.getName()));
        });
    }
    @Override
    public Long getProductCountByCategoryId(Long categoryId) {
        return mdmItemApi.getItemCountByCategoryId(categoryId).getCheckedData();
        return productMapper.selectCountByCategoryId(categoryId);
    }
    @Override
    public Long getProductCountByUnitId(Long unitId) {
        return mdmItemApi.getItemCountByUnitMeasureId(unitId).getCheckedData();
        return productMapper.selectCountByUnitId(unitId);
    }
    // ==================== ç§æœ‰æ–¹æ³• ====================
    /**
     * æ ¡éªŒäº§å“å­˜åœ¨
     */
    private void validateProductExists(Long id) {
        mdmItemApi.validateItemExists(id).getCheckedData();
    }
    /**
     * æ ¡éªŒäº§å“æ¡ç å”¯ä¸€
     */
    private void validateProductBarCodeUnique(Long id, String barCode) {
        if (barCode == null) {
            return;
        }
        MdmItemRespDTO existItem = mdmItemApi.getItemByBarCode(barCode).getCheckedData();
        if (existItem != null && !existItem.getId().equals(id)) {
            throw exception(PRODUCT_BAR_CODE_EXISTS);
        }
    }
    /**
     * æž„建创建请求 DTO
     * æ³¨æ„ï¼šProductSaveReqVO ä½¿ç”¨ standard å­—段作为规格,MDM ä½¿ç”¨ specification
     */
    private MdmItemCreateReqDTO buildCreateReqDTO(ProductSaveReqVO saveReqVO) {
        MdmItemCreateReqDTO createReqDTO = new MdmItemCreateReqDTO();
        createReqDTO.setCode(saveReqVO.getBarCode()); // ä½¿ç”¨æ¡ç ä½œä¸ºç¼–码
        createReqDTO.setName(saveReqVO.getName());
        createReqDTO.setBarCode(saveReqVO.getBarCode());
        createReqDTO.setSpecification(saveReqVO.getStandard()); // standard -> specification
        createReqDTO.setCategoryId(saveReqVO.getCategoryId());
        createReqDTO.setUnitMeasureId(saveReqVO.getUnitId()); // unitId -> unitMeasureId
        createReqDTO.setItemType(MDM_ITEM_TYPE_PRODUCT);
        createReqDTO.setStatus(saveReqVO.getStatus() != null ? saveReqVO.getStatus() : CommonStatusEnum.ENABLE.getStatus());
        createReqDTO.setPurchasePrice(saveReqVO.getPurchasePrice());
        createReqDTO.setSalesPrice(saveReqVO.getSalePrice());
        createReqDTO.setCostPrice(saveReqVO.getMinPrice()); // minPrice -> costPrice
        createReqDTO.setExpiryDay(saveReqVO.getExpiryDay());
        createReqDTO.setWeight(saveReqVO.getWeight());
        createReqDTO.setRemark(saveReqVO.getRemark());
        return createReqDTO;
    }
    /**
     * æž„建更新请求 DTO
     */
    private MdmItemUpdateReqDTO buildUpdateReqDTO(ProductSaveReqVO saveReqVO) {
        MdmItemUpdateReqDTO updateReqDTO = new MdmItemUpdateReqDTO();
        updateReqDTO.setId(saveReqVO.getId());
        updateReqDTO.setCode(saveReqVO.getBarCode());
        updateReqDTO.setName(saveReqVO.getName());
        updateReqDTO.setBarCode(saveReqVO.getBarCode());
        updateReqDTO.setSpecification(saveReqVO.getStandard()); // standard -> specification
        updateReqDTO.setCategoryId(saveReqVO.getCategoryId());
        updateReqDTO.setUnitMeasureId(saveReqVO.getUnitId()); // unitId -> unitMeasureId
        updateReqDTO.setStatus(saveReqVO.getStatus());
        updateReqDTO.setPurchasePrice(saveReqVO.getPurchasePrice());
        updateReqDTO.setSalesPrice(saveReqVO.getSalePrice());
        updateReqDTO.setCostPrice(saveReqVO.getMinPrice());
        updateReqDTO.setExpiryDay(saveReqVO.getExpiryDay());
        updateReqDTO.setWeight(saveReqVO.getWeight());
        updateReqDTO.setRemark(saveReqVO.getRemark());
        return updateReqDTO;
    }
    /**
     * æž„建产品 VO åˆ—表(包含分类和单位名称)
     * æ³¨æ„ï¼šMDM å’Œ ERP å­—段名有差异,需要手动映射
     */
    private List<ErpProductRespVO> buildProductVOList(List<MdmItemRespDTO> itemList) {
        if (CollUtil.isEmpty(itemList)) {
            return Collections.emptyList();
        }
        // 1. èŽ·å–åˆ†ç±»å’Œå•ä½ä¿¡æ¯
        Set<Long> categoryIds = convertSet(itemList, MdmItemRespDTO::getCategoryId);
        Set<Long> unitIds = convertSet(itemList, MdmItemRespDTO::getUnitMeasureId);
        Map<Long, MdmItemCategoryRespDTO> categoryMap = CollUtil.isEmpty(categoryIds)
                ? Collections.emptyMap()
                : convertMap(mdmItemCategoryApi.getItemCategoryList(categoryIds).getCheckedData(), MdmItemCategoryRespDTO::getId);
        Map<Long, MdmUnitMeasureRespDTO> unitMap = CollUtil.isEmpty(unitIds)
                ? Collections.emptyMap()
                : convertMap(mdmUnitMeasureApi.getUnitMeasureList(unitIds).getCheckedData(), MdmUnitMeasureRespDTO::getId);
        // 2. æ‰‹åŠ¨è½¬æ¢å¹¶å¡«å……å…³è”ä¿¡æ¯ï¼ˆå¤„ç†å­—æ®µåå·®å¼‚ï¼‰
        List<ErpProductRespVO> result = new ArrayList<>();
        for (MdmItemRespDTO mdmItem : itemList) {
            ErpProductRespVO product = new ErpProductRespVO();
            product.setId(mdmItem.getId());
            product.setName(mdmItem.getName());
            product.setBarCode(mdmItem.getBarCode());
            product.setStandard(mdmItem.getSpecification()); // specification -> standard
            product.setCategoryId(mdmItem.getCategoryId());
            product.setUnitId(mdmItem.getUnitMeasureId()); // unitMeasureId -> unitId
            product.setStatus(mdmItem.getStatus());
            product.setPurchasePrice(mdmItem.getPurchasePrice());
            product.setSalePrice(mdmItem.getSalesPrice()); // salesPrice -> salePrice
            product.setMinPrice(mdmItem.getCostPrice()); // costPrice -> minPrice
            product.setExpiryDay(mdmItem.getExpiryDay());
            product.setWeight(mdmItem.getWeight());
            product.setRemark(mdmItem.getRemark());
            // è®¾ç½®åˆ†ç±»å’Œå•位名称
            MapUtils.findAndThen(categoryMap, mdmItem.getCategoryId(),
                    category -> product.setCategoryName(category.getName()));
            MapUtils.findAndThen(unitMap, mdmItem.getUnitMeasureId(),
                    unit -> product.setUnitName(unit.getName()));
            result.add(product);
        }
        return result;
    }
    /**
     * è½¬æ¢ MDM ç‰©æ–™ä¸º ERP äº§å“ DO
     * æ³¨æ„ï¼šå­—段名差异需要手动映射
     */
    private ErpProductDO convertToErpProductDO(MdmItemRespDTO mdmItem) {
        if (mdmItem == null) {
            return null;
        }
        ErpProductDO product = new ErpProductDO();
        product.setId(mdmItem.getId());
        product.setName(mdmItem.getName());
        product.setBarCode(mdmItem.getBarCode());
        product.setStandard(mdmItem.getSpecification()); // specification -> standard
        product.setCategoryId(mdmItem.getCategoryId());
        product.setUnitId(mdmItem.getUnitMeasureId()); // unitMeasureId -> unitId
        product.setStatus(mdmItem.getStatus());
        product.setPurchasePrice(mdmItem.getPurchasePrice());
        product.setSalePrice(mdmItem.getSalesPrice()); // salesPrice -> salePrice
        product.setMinPrice(mdmItem.getCostPrice()); // costPrice -> minPrice
        product.setExpiryDay(mdmItem.getExpiryDay());
        product.setWeight(mdmItem.getWeight());
        product.setRemark(mdmItem.getRemark());
        return product;
    }
    /**
     * æ‰¹é‡è½¬æ¢ MDM ç‰©æ–™åˆ—表为 ERP äº§å“åˆ—表
     */
    private List<ErpProductDO> convertToErpProductDOList(List<MdmItemRespDTO> mdmItemList) {
        if (CollUtil.isEmpty(mdmItemList)) {
            return Collections.emptyList();
        }
        List<ErpProductDO> result = new ArrayList<>();
        for (MdmItemRespDTO mdmItem : mdmItemList) {
            result.add(convertToErpProductDO(mdmItem));
        }
        return result;
    }
}
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInService.java
@@ -42,22 +42,6 @@
    void updatePurchaseInStatus(Long id, Integer status);
    /**
     * æäº¤é‡‡è´­å…¥åº“审批
     *
     * @param id ç¼–号
     * @param userId ç”¨æˆ·ç¼–号
     */
    void submitPurchaseIn(Long id, Long userId);
    /**
     * æ›´æ–°é‡‡è´­å…¥åº“的审批状态
     *
     * @param id ç¼–号
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     */
    void updatePurchaseInAuditStatus(Long id, Integer bpmResult);
    /**
     * æ›´æ–°é‡‡è´­å…¥åº“的付款金额
     *
     * @param id ç¼–号
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseInServiceImpl.java
@@ -5,8 +5,6 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.in.ErpPurchaseInSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
@@ -24,7 +22,6 @@
import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -40,6 +37,8 @@
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
// TODO èŠ‹è‰¿ï¼šè®°å½•æ“ä½œæ—¥å¿—
/**
 * ERP é‡‡è´­å…¥åº“ Service å®žçŽ°ç±»
 *
@@ -47,13 +46,7 @@
 */
@Service
@Validated
@Slf4j
public class ErpPurchaseInServiceImpl implements ErpPurchaseInService {
    /**
     * BPM é‡‡è´­å…¥åº“审批流程标识
     */
    public static final String BPM_PROCESS_DEFINITION_KEY = "erp-purchase-in-audit";
    @Resource
    private ErpPurchaseInMapper purchaseInMapper;
@@ -76,9 +69,6 @@
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createPurchaseIn(ErpPurchaseInSaveReqVO createReqVO) {
@@ -96,7 +86,7 @@
        // 2.1 æ’入入库
        ErpPurchaseInDO purchaseIn = BeanUtils.toBean(createReqVO, ErpPurchaseInDO.class, in -> in
                .setNo(no).setStatus(ErpAuditStatus.DRAFT.getStatus()))
                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()))
                .setOrderNo(purchaseOrder.getNo()).setSupplierId(purchaseOrder.getSupplierId());
        calculateTotalPrice(purchaseIn, purchaseInItems);
        purchaseInMapper.insert(purchaseIn);
@@ -191,15 +181,9 @@
                : ErpStockRecordBizTypeEnum.PURCHASE_IN_CANCEL.getType();
        purchaseInItems.forEach(purchaseInItem -> {
            BigDecimal count = approve ? purchaseInItem.getCount() : purchaseInItem.getCount().negate();
            ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
            stockRecordBO.setProductId(purchaseInItem.getProductId());
            stockRecordBO.setWarehouseId(purchaseInItem.getWarehouseId());
            stockRecordBO.setCount(count);
            stockRecordBO.setBizType(bizType);
            stockRecordBO.setBizId(purchaseInItem.getInId());
            stockRecordBO.setBizItemId(purchaseInItem.getId());
            stockRecordBO.setBizNo(purchaseIn.getNo());
            stockRecordService.createStockRecord(stockRecordBO);
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    purchaseInItem.getProductId(), purchaseInItem.getWarehouseId(), count,
                    bizType, purchaseInItem.getInId(), purchaseInItem.getId(), purchaseIn.getNo()));
        });
    }
@@ -319,85 +303,6 @@
            return Collections.emptyList();
        }
        return purchaseInItemMapper.selectListByInIds(inIds);
    }
    // ==================== BPM æµç¨‹å®¡æ‰¹ ====================
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitPurchaseIn(Long id, Long userId) {
        // 1. æ ¡éªŒé‡‡è´­å…¥åº“处于草稿状态
        ErpPurchaseInDO purchaseIn = validatePurchaseInExists(id);
        if (ObjectUtil.notEqual(purchaseIn.getStatus(), ErpAuditStatus.DRAFT.getStatus())) {
            throw exception(PURCHASE_IN_SUBMIT_FAIL_NOT_DRAFT);
        }
        // 2. åˆ›å»ºé‡‡è´­å…¥åº“审批流程实例
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY)
                .setBusinessKey(String.valueOf(id)));
        // 3. æ›´æ–°é‡‡è´­å…¥åº“工作流编号、状态
        purchaseInMapper.updateById(new ErpPurchaseInDO()
                .setId(id)
                .setProcessInstanceId(processInstanceId)
                .setStatus(ErpAuditStatus.PROCESS.getStatus()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchaseInAuditStatus(Long id, Integer bpmResult) {
        // 1. æ ¡éªŒé‡‡è´­å…¥åº“存在
        ErpPurchaseInDO purchaseIn = validatePurchaseInExists(id);
        // 2. æ ¡éªŒé‡‡è´­å…¥åº“处于审批中
        if (ObjectUtil.notEqual(purchaseIn.getStatus(), ErpAuditStatus.PROCESS.getStatus())) {
            log.error("[updatePurchaseInAuditStatus][purchaseIn({}) ä¸å¤„于审批中,无法更新审批结果({})]",
                    purchaseIn.getId(), bpmResult);
            throw exception(PURCHASE_IN_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
        }
        // 3. æ›´æ–°é‡‡è´­å…¥åº“审批结果
        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
        purchaseInMapper.updateById(new ErpPurchaseInDO()
                .setId(id)
                .setStatus(auditStatus));
        // 4. å®¡æ‰¹é€šè¿‡æ—¶å˜æ›´åº“å­˜
        if (ErpAuditStatus.APPROVE.getStatus().equals(auditStatus)) {
            List<ErpPurchaseInItemDO> purchaseInItems = purchaseInItemMapper.selectListByInId(id);
            purchaseInItems.forEach(purchaseInItem -> {
                ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
                stockRecordBO.setProductId(purchaseInItem.getProductId());
                stockRecordBO.setWarehouseId(purchaseInItem.getWarehouseId());
                stockRecordBO.setCount(purchaseInItem.getCount());
                stockRecordBO.setBizType(ErpStockRecordBizTypeEnum.PURCHASE_IN.getType());
                stockRecordBO.setBizId(purchaseInItem.getInId());
                stockRecordBO.setBizItemId(purchaseInItem.getId());
                stockRecordBO.setBizNo(purchaseIn.getNo());
                stockRecordService.createStockRecord(stockRecordBO);
            });
        }
    }
    /**
     * å°† BPM å®¡æ‰¹ç»“果转换为 ERP å®¡æ‰¹çŠ¶æ€
     *
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     * @return ERP å®¡æ‰¹çŠ¶æ€
     */
    private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
        // BPM å®¡æ‰¹ç»“果:2=通过,3=不通过,4=已取消
        if (bpmResult == 2) {
            return ErpAuditStatus.APPROVE.getStatus();
        } else if (bpmResult == 3) {
            return ErpAuditStatus.REJECT.getStatus();
        } else if (bpmResult == 4) {
            return ErpAuditStatus.DRAFT.getStatus();
        } else {
            log.warn("[convertBpmResultToAuditStatus][未知的 BPM å®¡æ‰¹ç»“æžœ({})]", bpmResult);
            return ErpAuditStatus.PROCESS.getStatus();
        }
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderService.java
@@ -43,22 +43,6 @@
    void updatePurchaseOrderStatus(Long id, Integer status);
    /**
     * æäº¤é‡‡è´­è®¢å•审批
     *
     * @param id ç¼–号
     * @param userId ç”¨æˆ·ç¼–号
     */
    void submitPurchaseOrder(Long id, Long userId);
    /**
     * æ›´æ–°é‡‡è´­è®¢å•的审批状态
     *
     * @param id ç¼–号
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     */
    void updatePurchaseOrderAuditStatus(Long id, Integer bpmResult);
    /**
     * æ›´æ–°é‡‡è´­è®¢å•的入库数量
     *
     * @param id ç¼–号
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseOrderServiceImpl.java
@@ -5,8 +5,6 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.purchase.vo.order.ErpPurchaseOrderSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
@@ -19,7 +17,6 @@
import cn.iocoder.yudao.module.erp.service.finance.ErpAccountService;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -34,6 +31,8 @@
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
// TODO èŠ‹è‰¿ï¼šè®°å½•æ“ä½œæ—¥å¿—
/**
 * ERP é‡‡è´­è®¢å• Service å®žçŽ°ç±»
 *
@@ -41,13 +40,7 @@
 */
@Service
@Validated
@Slf4j
public class ErpPurchaseOrderServiceImpl implements ErpPurchaseOrderService {
    /**
     * BPM é‡‡è´­è®¢å•审批流程标识
     */
    public static final String BPM_PROCESS_DEFINITION_KEY = "erp-purchase-order-audit";
    @Resource
    private ErpPurchaseOrderMapper purchaseOrderMapper;
@@ -63,9 +56,6 @@
    private ErpSupplierService supplierService;
    @Resource
    private ErpAccountService accountService;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -86,7 +76,7 @@
        // 2.1 æ’入订单
        ErpPurchaseOrderDO purchaseOrder = BeanUtils.toBean(createReqVO, ErpPurchaseOrderDO.class, in -> in
                .setNo(no).setStatus(ErpAuditStatus.DRAFT.getStatus()));
                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()));
        calculateTotalPrice(purchaseOrder, purchaseOrderItems);
        purchaseOrderMapper.insert(purchaseOrder);
        // 2.2 æ’入订单项
@@ -300,69 +290,6 @@
            return Collections.emptyList();
        }
        return purchaseOrderItemMapper.selectListByOrderIds(orderIds);
    }
    // ==================== BPM æµç¨‹å®¡æ‰¹ ====================
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitPurchaseOrder(Long id, Long userId) {
        // 1. æ ¡éªŒé‡‡è´­è®¢å•处于草稿状态
        ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
        if (ObjectUtil.notEqual(purchaseOrder.getStatus(), ErpAuditStatus.DRAFT.getStatus())) {
            throw exception(PURCHASE_ORDER_SUBMIT_FAIL_NOT_DRAFT);
        }
        // 2. åˆ›å»ºé‡‡è´­è®¢å•审批流程实例
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY)
                .setBusinessKey(String.valueOf(id)));
        // 3. æ›´æ–°é‡‡è´­è®¢å•工作流编号、状态
        purchaseOrderMapper.updateById(new ErpPurchaseOrderDO()
                .setId(id)
                .setProcessInstanceId(processInstanceId)
                .setStatus(ErpAuditStatus.PROCESS.getStatus()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updatePurchaseOrderAuditStatus(Long id, Integer bpmResult) {
        // 1. æ ¡éªŒé‡‡è´­è®¢å•存在
        ErpPurchaseOrderDO purchaseOrder = validatePurchaseOrderExists(id);
        // 2. æ ¡éªŒé‡‡è´­è®¢å•处于审批中
        if (ObjectUtil.notEqual(purchaseOrder.getStatus(), ErpAuditStatus.PROCESS.getStatus())) {
            log.error("[updatePurchaseOrderAuditStatus][purchaseOrder({}) ä¸å¤„于审批中,无法更新审批结果({})]",
                    purchaseOrder.getId(), bpmResult);
            throw exception(PURCHASE_ORDER_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
        }
        // 3. æ›´æ–°é‡‡è´­è®¢å•审批结果
        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
        purchaseOrderMapper.updateById(new ErpPurchaseOrderDO()
                .setId(id)
                .setStatus(auditStatus));
    }
    /**
     * å°† BPM å®¡æ‰¹ç»“果转换为 ERP å®¡æ‰¹çŠ¶æ€
     *
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     * @return ERP å®¡æ‰¹çŠ¶æ€
     */
    private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
        // BPM å®¡æ‰¹ç»“果:2=通过,3=不通过,4=已取消
        if (bpmResult == 2) {
            return ErpAuditStatus.APPROVE.getStatus();
        } else if (bpmResult == 3) {
            return ErpAuditStatus.REJECT.getStatus();
        } else if (bpmResult == 4) {
            return ErpAuditStatus.DRAFT.getStatus();
        } else {
            log.warn("[convertBpmResultToAuditStatus][未知的 BPM å®¡æ‰¹ç»“æžœ({})]", bpmResult);
            return ErpAuditStatus.PROCESS.getStatus();
        }
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/ErpPurchaseReturnServiceImpl.java
@@ -177,15 +177,9 @@
                : ErpStockRecordBizTypeEnum.PURCHASE_RETURN_CANCEL.getType();
        purchaseReturnItems.forEach(purchaseReturnItem -> {
            BigDecimal count = approve ? purchaseReturnItem.getCount().negate() : purchaseReturnItem.getCount();
            ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
            stockRecordBO.setProductId(purchaseReturnItem.getProductId());
            stockRecordBO.setWarehouseId(purchaseReturnItem.getWarehouseId());
            stockRecordBO.setCount(count);
            stockRecordBO.setBizType(bizType);
            stockRecordBO.setBizId(purchaseReturnItem.getReturnId());
            stockRecordBO.setBizItemId(purchaseReturnItem.getId());
            stockRecordBO.setBizNo(purchaseReturn.getNo());
            stockRecordService.createStockRecord(stockRecordBO);
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    purchaseReturnItem.getProductId(), purchaseReturnItem.getWarehouseId(), count,
                    bizType, purchaseReturnItem.getReturnId(), purchaseReturnItem.getId(), purchaseReturn.getNo()));
        });
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseInStatusListener.java
ÎļþÒÑɾ³ý
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/purchase/listener/ErpPurchaseOrderStatusListener.java
ÎļþÒÑɾ³ý
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderService.java
@@ -43,22 +43,6 @@
    void updateSaleOrderStatus(Long id, Integer status);
    /**
     * æäº¤é”€å”®è®¢å•审批
     *
     * @param id ç¼–号
     * @param userId ç”¨æˆ·ç¼–号
     */
    void submitSaleOrder(Long id, Long userId);
    /**
     * æ›´æ–°é”€å”®è®¢å•的审批状态
     *
     * @param id ç¼–号
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     */
    void updateSaleOrderAuditStatus(Long id, Integer bpmResult);
    /**
     * æ›´æ–°é”€å”®è®¢å•的出库数量
     *
     * @param id ç¼–号
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOrderServiceImpl.java
@@ -5,8 +5,6 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.order.ErpSaleOrderSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
@@ -20,7 +18,6 @@
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
@@ -35,6 +32,8 @@
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
// TODO èŠ‹è‰¿ï¼šè®°å½•æ“ä½œæ—¥å¿—
/**
 * ERP é”€å”®è®¢å• Service å®žçŽ°ç±»
 *
@@ -42,13 +41,7 @@
 */
@Service
@Validated
@Slf4j
public class ErpSaleOrderServiceImpl implements ErpSaleOrderService {
    /**
     * BPM é”€å”®è®¢å•审批流程标识
     */
    public static final String BPM_PROCESS_DEFINITION_KEY = "erp-sale-order-audit";
    @Resource
    private ErpSaleOrderMapper saleOrderMapper;
@@ -67,9 +60,6 @@
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
@@ -94,7 +84,7 @@
        // 2.1 æ’入订单
        ErpSaleOrderDO saleOrder = BeanUtils.toBean(createReqVO, ErpSaleOrderDO.class, in -> in
                .setNo(no).setStatus(ErpAuditStatus.DRAFT.getStatus()));
                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()));
        calculateTotalPrice(saleOrder, saleOrderItems);
        saleOrderMapper.insert(saleOrder);
        // 2.2 æ’入订单项
@@ -312,69 +302,6 @@
            return Collections.emptyList();
        }
        return saleOrderItemMapper.selectListByOrderIds(orderIds);
    }
    // ==================== BPM æµç¨‹å®¡æ‰¹ ====================
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitSaleOrder(Long id, Long userId) {
        // 1. æ ¡éªŒé”€å”®è®¢å•处于草稿状态
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
        if (ObjectUtil.notEqual(saleOrder.getStatus(), ErpAuditStatus.DRAFT.getStatus())) {
            throw exception(SALE_ORDER_SUBMIT_FAIL_NOT_DRAFT);
        }
        // 2. åˆ›å»ºé”€å”®è®¢å•审批流程实例
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY)
                .setBusinessKey(String.valueOf(id)));
        // 3. æ›´æ–°é”€å”®è®¢å•工作流编号、状态
        saleOrderMapper.updateById(new ErpSaleOrderDO()
                .setId(id)
                .setProcessInstanceId(processInstanceId)
                .setStatus(ErpAuditStatus.PROCESS.getStatus()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSaleOrderAuditStatus(Long id, Integer bpmResult) {
        // 1. æ ¡éªŒé”€å”®è®¢å•存在
        ErpSaleOrderDO saleOrder = validateSaleOrderExists(id);
        // 2. æ ¡éªŒé”€å”®è®¢å•处于审批中
        if (ObjectUtil.notEqual(saleOrder.getStatus(), ErpAuditStatus.PROCESS.getStatus())) {
            log.error("[updateSaleOrderAuditStatus][saleOrder({}) ä¸å¤„于审批中,无法更新审批结果({})]",
                    saleOrder.getId(), bpmResult);
            throw exception(SALE_ORDER_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
        }
        // 3. æ›´æ–°é”€å”®è®¢å•审批结果
        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
        saleOrderMapper.updateById(new ErpSaleOrderDO()
                .setId(id)
                .setStatus(auditStatus));
    }
    /**
     * å°† BPM å®¡æ‰¹ç»“果转换为 ERP å®¡æ‰¹çŠ¶æ€
     *
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     * @return ERP å®¡æ‰¹çŠ¶æ€
     */
    private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
        // BPM å®¡æ‰¹ç»“果:2=通过,3=不通过,4=已取消
        if (bpmResult == 2) {
            return ErpAuditStatus.APPROVE.getStatus();
        } else if (bpmResult == 3) {
            return ErpAuditStatus.REJECT.getStatus();
        } else if (bpmResult == 4) {
            return ErpAuditStatus.DRAFT.getStatus();
        } else {
            log.warn("[convertBpmResultToAuditStatus][未知的 BPM å®¡æ‰¹ç»“æžœ({})]", bpmResult);
            return ErpAuditStatus.PROCESS.getStatus();
        }
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutService.java
@@ -42,22 +42,6 @@
    void updateSaleOutStatus(Long id, Integer status);
    /**
     * æäº¤é”€å”®å‡ºåº“审批
     *
     * @param id ç¼–号
     * @param userId ç”¨æˆ·ç¼–号
     */
    void submitSaleOut(Long id, Long userId);
    /**
     * æ›´æ–°é”€å”®å‡ºåº“的审批状态
     *
     * @param id ç¼–号
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     */
    void updateSaleOutAuditStatus(Long id, Integer bpmResult);
    /**
     * æ›´æ–°é”€å”®å‡ºåº“的收款金额
     *
     * @param id ç¼–号
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleOutServiceImpl.java
@@ -5,8 +5,6 @@
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.number.MoneyUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.bpm.api.task.BpmProcessInstanceApi;
import cn.iocoder.yudao.module.bpm.api.task.dto.BpmProcessInstanceCreateReqDTO;
import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutPageReqVO;
import cn.iocoder.yudao.module.erp.controller.admin.sale.vo.out.ErpSaleOutSaveReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.product.ErpProductDO;
@@ -24,7 +22,6 @@
import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -40,6 +37,8 @@
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
// TODO èŠ‹è‰¿ï¼šè®°å½•æ“ä½œæ—¥å¿—
/**
 * ERP é”€å”®å‡ºåº“ Service å®žçŽ°ç±»
 *
@@ -47,13 +46,7 @@
 */
@Service
@Validated
@Slf4j
public class ErpSaleOutServiceImpl implements ErpSaleOutService {
    /**
     * BPM é”€å”®å‡ºåº“审批流程标识
     */
    public static final String BPM_PROCESS_DEFINITION_KEY = "erp-sale-out-audit";
    @Resource
    private ErpSaleOutMapper saleOutMapper;
@@ -76,9 +69,6 @@
    @Resource
    private AdminUserApi adminUserApi;
    @Resource
    private BpmProcessInstanceApi bpmProcessInstanceApi;
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long createSaleOut(ErpSaleOutSaveReqVO createReqVO) {
@@ -100,7 +90,7 @@
        // 2.1 æ’入出库
        ErpSaleOutDO saleOut = BeanUtils.toBean(createReqVO, ErpSaleOutDO.class, in -> in
                .setNo(no).setStatus(ErpAuditStatus.DRAFT.getStatus()))
                .setNo(no).setStatus(ErpAuditStatus.PROCESS.getStatus()))
                .setOrderNo(saleOrder.getNo()).setCustomerId(saleOrder.getCustomerId());
        calculateTotalPrice(saleOut, saleOutItems);
        saleOutMapper.insert(saleOut);
@@ -199,15 +189,9 @@
                : ErpStockRecordBizTypeEnum.SALE_OUT_CANCEL.getType();
        saleOutItems.forEach(saleOutItem -> {
            BigDecimal count = approve ? saleOutItem.getCount().negate() : saleOutItem.getCount();
            ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
            stockRecordBO.setProductId(saleOutItem.getProductId());
            stockRecordBO.setWarehouseId(saleOutItem.getWarehouseId());
            stockRecordBO.setCount(count);
            stockRecordBO.setBizType(bizType);
            stockRecordBO.setBizId(saleOutItem.getOutId());
            stockRecordBO.setBizItemId(saleOutItem.getId());
            stockRecordBO.setBizNo(saleOut.getNo());
            stockRecordService.createStockRecord(stockRecordBO);
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    saleOutItem.getProductId(), saleOutItem.getWarehouseId(), count,
                    bizType, saleOutItem.getOutId(), saleOutItem.getId(), saleOut.getNo()));
        });
    }
@@ -327,85 +311,6 @@
            return Collections.emptyList();
        }
        return saleOutItemMapper.selectListByOutIds(outIds);
    }
    // ==================== BPM æµç¨‹å®¡æ‰¹ ====================
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void submitSaleOut(Long id, Long userId) {
        // 1. æ ¡éªŒé”€å”®å‡ºåº“处于草稿状态
        ErpSaleOutDO saleOut = validateSaleOutExists(id);
        if (ObjectUtil.notEqual(saleOut.getStatus(), ErpAuditStatus.DRAFT.getStatus())) {
            throw exception(SALE_OUT_SUBMIT_FAIL_NOT_DRAFT);
        }
        // 2. åˆ›å»ºé”€å”®å‡ºåº“审批流程实例
        String processInstanceId = bpmProcessInstanceApi.createProcessInstance(userId, new BpmProcessInstanceCreateReqDTO()
                .setProcessDefinitionKey(BPM_PROCESS_DEFINITION_KEY)
                .setBusinessKey(String.valueOf(id)));
        // 3. æ›´æ–°é”€å”®å‡ºåº“工作流编号、状态
        saleOutMapper.updateById(new ErpSaleOutDO()
                .setId(id)
                .setProcessInstanceId(processInstanceId)
                .setStatus(ErpAuditStatus.PROCESS.getStatus()));
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateSaleOutAuditStatus(Long id, Integer bpmResult) {
        // 1. æ ¡éªŒé”€å”®å‡ºåº“存在
        ErpSaleOutDO saleOut = validateSaleOutExists(id);
        // 2. æ ¡éªŒé”€å”®å‡ºåº“处于审批中
        if (ObjectUtil.notEqual(saleOut.getStatus(), ErpAuditStatus.PROCESS.getStatus())) {
            log.error("[updateSaleOutAuditStatus][saleOut({}) ä¸å¤„于审批中,无法更新审批结果({})]",
                    saleOut.getId(), bpmResult);
            throw exception(SALE_OUT_UPDATE_AUDIT_STATUS_FAIL_NOT_PROCESS);
        }
        // 3. æ›´æ–°é”€å”®å‡ºåº“审批结果
        Integer auditStatus = convertBpmResultToAuditStatus(bpmResult);
        saleOutMapper.updateById(new ErpSaleOutDO()
                .setId(id)
                .setStatus(auditStatus));
        // 4. å®¡æ‰¹é€šè¿‡æ—¶å˜æ›´åº“存(减少库存)
        if (ErpAuditStatus.APPROVE.getStatus().equals(auditStatus)) {
            List<ErpSaleOutItemDO> saleOutItems = saleOutItemMapper.selectListByOutId(id);
            saleOutItems.forEach(saleOutItem -> {
                ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
                stockRecordBO.setProductId(saleOutItem.getProductId());
                stockRecordBO.setWarehouseId(saleOutItem.getWarehouseId());
                stockRecordBO.setCount(saleOutItem.getCount().negate());
                stockRecordBO.setBizType(ErpStockRecordBizTypeEnum.SALE_OUT.getType());
                stockRecordBO.setBizId(saleOutItem.getOutId());
                stockRecordBO.setBizItemId(saleOutItem.getId());
                stockRecordBO.setBizNo(saleOut.getNo());
                stockRecordService.createStockRecord(stockRecordBO);
            });
        }
    }
    /**
     * å°† BPM å®¡æ‰¹ç»“果转换为 ERP å®¡æ‰¹çŠ¶æ€
     *
     * @param bpmResult BPM å®¡æ‰¹ç»“æžœ
     * @return ERP å®¡æ‰¹çŠ¶æ€
     */
    private Integer convertBpmResultToAuditStatus(Integer bpmResult) {
        // BPM å®¡æ‰¹ç»“果:2=通过,3=不通过,4=已取消
        if (bpmResult == 2) {
            return ErpAuditStatus.APPROVE.getStatus();
        } else if (bpmResult == 3) {
            return ErpAuditStatus.REJECT.getStatus();
        } else if (bpmResult == 4) {
            return ErpAuditStatus.DRAFT.getStatus();
        } else {
            log.warn("[convertBpmResultToAuditStatus][未知的 BPM å®¡æ‰¹ç»“æžœ({})]", bpmResult);
            return ErpAuditStatus.PROCESS.getStatus();
        }
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/ErpSaleReturnServiceImpl.java
@@ -189,15 +189,9 @@
                : ErpStockRecordBizTypeEnum.SALE_RETURN_CANCEL.getType();
        saleReturnItems.forEach(saleReturnItem -> {
            BigDecimal count = approve ? saleReturnItem.getCount() : saleReturnItem.getCount().negate();
            ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
            stockRecordBO.setProductId(saleReturnItem.getProductId());
            stockRecordBO.setWarehouseId(saleReturnItem.getWarehouseId());
            stockRecordBO.setCount(count);
            stockRecordBO.setBizType(bizType);
            stockRecordBO.setBizId(saleReturnItem.getReturnId());
            stockRecordBO.setBizItemId(saleReturnItem.getId());
            stockRecordBO.setBizNo(saleReturn.getNo());
            stockRecordService.createStockRecord(stockRecordBO);
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    saleReturnItem.getProductId(), saleReturnItem.getWarehouseId(), count,
                    bizType, saleReturnItem.getReturnId(), saleReturnItem.getId(), saleReturn.getNo()));
        });
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ErpSaleOrderStatusListener.java
ÎļþÒÑɾ³ý
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/sale/listener/ErpSaleOutStatusListener.java
ÎļþÒÑɾ³ý
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockCheckServiceImpl.java
@@ -135,15 +135,9 @@
                bizType = count.compareTo(BigDecimal.ZERO) > 0 ? ErpStockRecordBizTypeEnum.CHECK_MORE_IN_CANCEL.getType()
                        : ErpStockRecordBizTypeEnum.CHECK_LESS_OUT_CANCEL.getType();
            }
            ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
            stockRecordBO.setProductId(stockCheckItem.getProductId());
            stockRecordBO.setWarehouseId(stockCheckItem.getWarehouseId());
            stockRecordBO.setCount(count);
            stockRecordBO.setBizType(bizType);
            stockRecordBO.setBizId(stockCheckItem.getCheckId());
            stockRecordBO.setBizItemId(stockCheckItem.getId());
            stockRecordBO.setBizNo(stockCheck.getNo());
            stockRecordService.createStockRecord(stockRecordBO);
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    stockCheckItem.getProductId(), stockCheckItem.getWarehouseId(), count,
                    bizType, stockCheckItem.getCheckId(), stockCheckItem.getId(), stockCheck.getNo()));
        });
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockInServiceImpl.java
@@ -130,15 +130,9 @@
                : ErpStockRecordBizTypeEnum.OTHER_IN_CANCEL.getType();
        stockInItems.forEach(stockInItem -> {
            BigDecimal count = approve ? stockInItem.getCount() : stockInItem.getCount().negate();
            ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
            stockRecordBO.setProductId(stockInItem.getProductId());
            stockRecordBO.setWarehouseId(stockInItem.getWarehouseId());
            stockRecordBO.setCount(count);
            stockRecordBO.setBizType(bizType);
            stockRecordBO.setBizId(stockInItem.getInId());
            stockRecordBO.setBizItemId(stockInItem.getId());
            stockRecordBO.setBizNo(stockIn.getNo());
            stockRecordService.createStockRecord(stockRecordBO);
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    stockInItem.getProductId(), stockInItem.getWarehouseId(), count,
                    bizType, stockInItem.getInId(), stockInItem.getId(), stockIn.getNo()));
        });
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockMoveServiceImpl.java
@@ -128,24 +128,12 @@
        stockMoveItems.forEach(stockMoveItem -> {
            BigDecimal fromCount = approve ? stockMoveItem.getCount().negate() : stockMoveItem.getCount();
            BigDecimal toCount = approve ? stockMoveItem.getCount() : stockMoveItem.getCount().negate();
            ErpStockRecordCreateReqBO fromStockRecordBO = new ErpStockRecordCreateReqBO();
            fromStockRecordBO.setProductId(stockMoveItem.getProductId());
            fromStockRecordBO.setWarehouseId(stockMoveItem.getFromWarehouseId());
            fromStockRecordBO.setCount(fromCount);
            fromStockRecordBO.setBizType(fromBizType);
            fromStockRecordBO.setBizId(stockMoveItem.getMoveId());
            fromStockRecordBO.setBizItemId(stockMoveItem.getId());
            fromStockRecordBO.setBizNo(stockMove.getNo());
            stockRecordService.createStockRecord(fromStockRecordBO);
            ErpStockRecordCreateReqBO toStockRecordBO = new ErpStockRecordCreateReqBO();
            toStockRecordBO.setProductId(stockMoveItem.getProductId());
            toStockRecordBO.setWarehouseId(stockMoveItem.getToWarehouseId());
            toStockRecordBO.setCount(toCount);
            toStockRecordBO.setBizType(toBizType);
            toStockRecordBO.setBizId(stockMoveItem.getMoveId());
            toStockRecordBO.setBizItemId(stockMoveItem.getId());
            toStockRecordBO.setBizNo(stockMove.getNo());
            stockRecordService.createStockRecord(toStockRecordBO);
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    stockMoveItem.getProductId(), stockMoveItem.getFromWarehouseId(), fromCount,
                    fromBizType, stockMoveItem.getMoveId(), stockMoveItem.getId(), stockMove.getNo()));
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    stockMoveItem.getProductId(), stockMoveItem.getToWarehouseId(), toCount,
                    toBizType, stockMoveItem.getMoveId(), stockMoveItem.getId(), stockMove.getNo()));
        });
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockOutServiceImpl.java
@@ -131,15 +131,9 @@
                : ErpStockRecordBizTypeEnum.OTHER_OUT_CANCEL.getType();
        stockOutItems.forEach(stockOutItem -> {
            BigDecimal count = approve ? stockOutItem.getCount().negate() : stockOutItem.getCount();
            ErpStockRecordCreateReqBO stockRecordBO = new ErpStockRecordCreateReqBO();
            stockRecordBO.setProductId(stockOutItem.getProductId());
            stockRecordBO.setWarehouseId(stockOutItem.getWarehouseId());
            stockRecordBO.setCount(count);
            stockRecordBO.setBizType(bizType);
            stockRecordBO.setBizId(stockOutItem.getOutId());
            stockRecordBO.setBizItemId(stockOutItem.getId());
            stockRecordBO.setBizNo(stockOut.getNo());
            stockRecordService.createStockRecord(stockRecordBO);
            stockRecordService.createStockRecord(new ErpStockRecordCreateReqBO(
                    stockOutItem.getProductId(), stockOutItem.getWarehouseId(), count,
                    bizType, stockOutItem.getOutId(), stockOutItem.getId(), stockOut.getNo()));
        });
    }
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockRecordServiceImpl.java
@@ -1,33 +1,20 @@
package cn.iocoder.yudao.module.erp.service.stock;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.record.ErpStockRecordPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockRecordDO;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockRecordMapper;
import cn.iocoder.yudao.module.erp.service.stock.bo.ErpStockRecordCreateReqBO;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemSkuApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemSkuRespDTO;
import cn.iocoder.yudao.module.wms.api.inventory.WmsInventoryApi;
import cn.iocoder.yudao.module.wms.api.inventory.dto.WmsReceiptReqDTO;
import cn.iocoder.yudao.module.wms.api.inventory.dto.WmsShipmentReqDTO;
import cn.iocoder.yudao.module.wms.enums.inventory.WmsInventoryBizTypeEnum;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.List;
/**
 * ERP äº§å“åº“存明细 Service å®žçŽ°ç±»
 *
 * æ”¹é€ è¯´æ˜Žï¼šåº“存变更调用 WMS åº“å­˜ API
 * - å…¥åº“操作调用 WmsInventoryApi.receipt()
 * - å‡ºåº“操作调用 WmsInventoryApi.shipment()
 * - productId å¯¹åº” MDM itemId,通过 SKU API èŽ·å–é»˜è®¤ SKU
 *
 * @author èŠ‹é“æºç 
 */
@@ -40,12 +27,6 @@
    @Resource
    private ErpStockService stockService;
    @Resource
    private WmsInventoryApi wmsInventoryApi;
    @Resource
    private MdmItemSkuApi mdmItemSkuApi;
    @Override
    public ErpStockRecordDO getStockRecord(Long id) {
@@ -60,98 +41,13 @@
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void createStockRecord(ErpStockRecordCreateReqBO createReqBO) {
        // 1. èŽ·å–äº§å“çš„é»˜è®¤ SKU ID(ERP productId = MDM itemId)
        Long skuId = getDefaultSkuId(createReqBO.getProductId());
        if (skuId == null) {
            // å¦‚果没有 SKU,使用原有逻辑更新库存
            BigDecimal totalCount = stockService.updateStockCountIncrement(
                    createReqBO.getProductId(), createReqBO.getWarehouseId(), createReqBO.getCount());
            ErpStockRecordDO stockRecord = BeanUtils.toBean(createReqBO, ErpStockRecordDO.class)
                    .setTotalCount(totalCount);
            stockRecordMapper.insert(stockRecord);
            return;
        }
        // 2. æ ¹æ® count æ­£è´Ÿåˆ¤æ–­å…¥åº“还是出库
        BigDecimal count = createReqBO.getCount();
        if (count.compareTo(BigDecimal.ZERO) > 0) {
            // å…¥åº“操作
            WmsReceiptReqDTO receiptReq = buildReceiptReqDTO(createReqBO, skuId);
            wmsInventoryApi.receipt(receiptReq);
        } else if (count.compareTo(BigDecimal.ZERO) < 0) {
            // å‡ºåº“操作(数量转为正数)
            WmsShipmentReqDTO shipmentReq = buildShipmentReqDTO(createReqBO, skuId);
            wmsInventoryApi.shipment(shipmentReq);
        }
        // 3. åˆ›å»ºåº“存明细(记录操作)
        BigDecimal currentStock = wmsInventoryApi.getAvailableQuantity(skuId, createReqBO.getWarehouseId()).getCheckedData();
        // 1. æ›´æ–°åº“å­˜
        BigDecimal totalCount = stockService.updateStockCountIncrement(
                createReqBO.getProductId(), createReqBO.getWarehouseId(), createReqBO.getCount());
        // 2. åˆ›å»ºåº“存明细
        ErpStockRecordDO stockRecord = BeanUtils.toBean(createReqBO, ErpStockRecordDO.class)
                .setTotalCount(currentStock != null ? currentStock : BigDecimal.ZERO);
                .setTotalCount(totalCount);
        stockRecordMapper.insert(stockRecord);
    }
    /**
     * èŽ·å–äº§å“çš„é»˜è®¤ SKU ID
     */
    private Long getDefaultSkuId(Long productId) {
        if (productId == null) {
            return null;
        }
        List<MdmItemSkuRespDTO> skuList = mdmItemSkuApi.getItemSkuListByItemId(productId).getCheckedData();
        if (CollUtil.isEmpty(skuList)) {
            return null;
        }
        return skuList.get(0).getId();
    }
    /**
     * æž„建入库请求
     */
    private WmsReceiptReqDTO buildReceiptReqDTO(ErpStockRecordCreateReqBO createReqBO, Long skuId) {
        WmsReceiptReqDTO receiptReq = new WmsReceiptReqDTO();
        receiptReq.setBizType(convertBizType(createReqBO.getBizType()));
        receiptReq.setBizId(createReqBO.getBizId());
        receiptReq.setBizNo(createReqBO.getBizNo());
        receiptReq.setWarehouseId(createReqBO.getWarehouseId());
        receiptReq.setRemark(createReqBO.getRemark());
        WmsReceiptReqDTO.Item item = new WmsReceiptReqDTO.Item();
        item.setSkuId(skuId);
        item.setQuantity(createReqBO.getCount());
        receiptReq.setItems(List.of(item));
        return receiptReq;
    }
    /**
     * æž„建出库请求
     */
    private WmsShipmentReqDTO buildShipmentReqDTO(ErpStockRecordCreateReqBO createReqBO, Long skuId) {
        WmsShipmentReqDTO shipmentReq = new WmsShipmentReqDTO();
        shipmentReq.setBizType(convertBizType(createReqBO.getBizType()));
        shipmentReq.setBizId(createReqBO.getBizId());
        shipmentReq.setBizNo(createReqBO.getBizNo());
        shipmentReq.setWarehouseId(createReqBO.getWarehouseId());
        shipmentReq.setRemark(createReqBO.getRemark());
        WmsShipmentReqDTO.Item item = new WmsShipmentReqDTO.Item();
        item.setSkuId(skuId);
        item.setQuantity(createReqBO.getCount().negate()); // å‡ºåº“数量为正数
        shipmentReq.setItems(List.of(item));
        return shipmentReq;
    }
    /**
     * è½¬æ¢ä¸šåŠ¡ç±»åž‹ï¼ˆERP ä¸šåŠ¡ç±»åž‹æ˜ å°„åˆ° WMS ä¸šåŠ¡ç±»åž‹ï¼‰
     */
    private Integer convertBizType(Integer erpBizType) {
        // ERP ä¸šåŠ¡ç±»åž‹æ˜ å°„åˆ° WMS ä¸šåŠ¡ç±»åž‹
        // å¯ä»¥æ ¹æ®å®žé™…业务需求扩展映射关系
        if (erpBizType == null) {
            return WmsInventoryBizTypeEnum.OTHER.getType();
        }
        // é»˜è®¤è¿”回 OTHER ç±»åž‹ï¼ŒåŽç»­å¯æ ¹æ® ERP ä¸šåŠ¡ç±»åž‹æžšä¸¾æ‰©å±•
        return WmsInventoryBizTypeEnum.OTHER.getType();
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/ErpStockServiceImpl.java
@@ -1,34 +1,22 @@
package cn.iocoder.yudao.module.erp.service.stock;
import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.erp.controller.admin.stock.vo.stock.ErpStockPageReqVO;
import cn.iocoder.yudao.module.erp.dal.dataobject.stock.ErpStockDO;
import cn.iocoder.yudao.module.erp.dal.mysql.stock.ErpStockMapper;
import cn.iocoder.yudao.module.erp.service.product.ErpProductService;
import cn.iocoder.yudao.module.erp.service.stock.ErpWarehouseService;
import cn.iocoder.yudao.module.mdm.api.item.MdmItemSkuApi;
import cn.iocoder.yudao.module.mdm.api.item.dto.MdmItemSkuRespDTO;
import cn.iocoder.yudao.module.wms.api.inventory.WmsInventoryApi;
import cn.iocoder.yudao.module.wms.api.inventory.dto.WmsInventoryQueryReqDTO;
import cn.iocoder.yudao.module.wms.api.inventory.dto.WmsInventoryRespDTO;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import java.math.BigDecimal;
import java.util.List;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE;
import static cn.iocoder.yudao.module.erp.enums.ErrorCodeConstants.STOCK_COUNT_NEGATIVE2;
/**
 * ERP äº§å“åº“å­˜ Service å®žçŽ°ç±»
 *
 * æ”¹é€ è¯´æ˜Žï¼šERP åº“存调用 WMS åº“å­˜ API
 * - productId å¯¹åº” MDM itemId
 * - é€šè¿‡ MDM SKU API èŽ·å–ç‰©æ–™çš„é»˜è®¤ SKU
 * - åº“存操作调用 WMS Inventory API
 *
 * @author èŠ‹é“æºç 
 */
@@ -38,24 +26,18 @@
    /**
     * å…è®¸åº“存为负数
     *
     * TODO èŠ‹è‰¿ï¼šåŽç»­åšæˆ db é…ç½®
     */
    private static final Boolean NEGATIVE_STOCK_COUNT_ENABLE = false;
    @Resource
    private ErpProductService productService;
    @Resource
    private ErpWarehouseService warehouseService;
    @Resource
    private ErpStockMapper stockMapper;
    @Resource
    private WmsInventoryApi wmsInventoryApi;
    @Resource
    private MdmItemSkuApi mdmItemSkuApi;
    @Override
    public ErpStockDO getStock(Long id) {
@@ -64,117 +46,44 @@
    @Override
    public ErpStockDO getStock(Long productId, Long warehouseId) {
        // é€šè¿‡ MDM èŽ·å–äº§å“çš„é»˜è®¤ SKU,然后查询 WMS åº“å­˜
        Long skuId = getDefaultSkuId(productId);
        if (skuId == null) {
            return null;
        }
        // æŸ¥è¯¢ WMS åº“å­˜
        BigDecimal quantity = wmsInventoryApi.getAvailableQuantity(skuId, warehouseId).getCheckedData();
        if (quantity == null || quantity.compareTo(BigDecimal.ZERO) == 0) {
            return null;
        }
        // è½¬æ¢ä¸º ERP Stock DO
        ErpStockDO stock = new ErpStockDO();
        stock.setProductId(productId);
        stock.setWarehouseId(warehouseId);
        stock.setCount(quantity);
        return stock;
        return stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);
    }
    @Override
    public BigDecimal getStockCount(Long productId) {
        // é€šè¿‡ MDM èŽ·å–äº§å“çš„é»˜è®¤ SKU,然后查询 WMS åº“存总量
        Long skuId = getDefaultSkuId(productId);
        if (skuId == null) {
            return BigDecimal.ZERO;
        }
        // æŸ¥è¯¢è¯¥ SKU åœ¨æ‰€æœ‰ä»“库的库存
        WmsInventoryQueryReqDTO queryReq = new WmsInventoryQueryReqDTO();
        queryReq.setSkuIds(List.of(skuId));
        queryReq.setOnlyPositive(true);
        List<WmsInventoryRespDTO> inventories = wmsInventoryApi.getInventoryList(queryReq).getCheckedData();
        if (CollUtil.isEmpty(inventories)) {
            return BigDecimal.ZERO;
        }
        return inventories.stream()
                .map(WmsInventoryRespDTO::getQuantity)
                .filter(q -> q != null)
                .reduce(BigDecimal.ZERO, BigDecimal::add);
        BigDecimal count = stockMapper.selectSumByProductId(productId);
        return count != null ? count : BigDecimal.ZERO;
    }
    @Override
    public PageResult<ErpStockDO> getStockPage(ErpStockPageReqVO pageReqVO) {
        // æš‚时保留原逻辑,后续可改为调用 WMS åˆ†é¡µ API
        return stockMapper.selectPage(pageReqVO);
    }
    @Override
    public BigDecimal updateStockCountIncrement(Long productId, Long warehouseId, BigDecimal count) {
        // é€šè¿‡ MDM èŽ·å–äº§å“çš„é»˜è®¤ SKU
        Long skuId = getDefaultSkuId(productId);
        if (skuId == null) {
            throw exception(STOCK_COUNT_NEGATIVE2,
                    productService.getProduct(productId).getName(),
                    warehouseService.getWarehouse(warehouseId).getName());
        }
        // æŸ¥è¯¢å½“前库存
        BigDecimal currentStock = wmsInventoryApi.getAvailableQuantity(skuId, warehouseId).getCheckedData();
        if (currentStock == null) {
            currentStock = BigDecimal.ZERO;
        }
        // æ ¡éªŒåº“存是否充足(出库时)
        if (!NEGATIVE_STOCK_COUNT_ENABLE && count.compareTo(BigDecimal.ZERO) < 0) {
            BigDecimal afterStock = currentStock.add(count);
            if (afterStock.compareTo(BigDecimal.ZERO) < 0) {
                throw exception(STOCK_COUNT_NEGATIVE,
                        productService.getProduct(productId).getName(),
                        warehouseService.getWarehouse(warehouseId).getName(),
                        currentStock, count.abs());
            }
        }
        // TODO: è°ƒç”¨ WMS åº“存变更 API(需要 WMS æä¾› increment/decrement æ–¹æ³•)
        // æš‚时保留原逻辑,使用本地库存表
        // 1.1 æŸ¥è¯¢å½“前库存
        ErpStockDO stock = stockMapper.selectByProductIdAndWarehouseId(productId, warehouseId);
        if (stock == null) {
            stock = new ErpStockDO()
                    .setProductId(productId)
                    .setWarehouseId(warehouseId)
                    .setCount(BigDecimal.ZERO);
            stock = new ErpStockDO().setProductId(productId).setWarehouseId(warehouseId).setCount(BigDecimal.ZERO);
            stockMapper.insert(stock);
        }
        // 1.2 æ ¡éªŒåº“存是否充足
        if (!NEGATIVE_STOCK_COUNT_ENABLE && stock.getCount().add(count).compareTo(BigDecimal.ZERO) < 0) {
            throw exception(STOCK_COUNT_NEGATIVE, productService.getProduct(productId).getName(),
                    warehouseService.getWarehouse(warehouseId).getName(), stock.getCount(), count);
        }
        // 2. åº“存变更
        int updateCount = stockMapper.updateCountIncrement(stock.getId(), count, NEGATIVE_STOCK_COUNT_ENABLE);
        if (updateCount == 0) {
            throw exception(STOCK_COUNT_NEGATIVE2,
                    productService.getProduct(productId).getName(),
            // æ­¤æ—¶ä¸å¥½åŽ»æŸ¥è¯¢æœ€æ–°åº“å­˜ï¼Œæ‰€ä»¥ç›´æŽ¥æŠ›å‡ºè¯¥æç¤ºï¼Œä¸æä¾›å…·ä½“åº“å­˜æ•°å­—
            throw exception(STOCK_COUNT_NEGATIVE2, productService.getProduct(productId).getName(),
                    warehouseService.getWarehouse(warehouseId).getName());
        }
        // 3. è¿”回最新库存
        return stock.getCount().add(count);
    }
    /**
     * èŽ·å–äº§å“çš„é»˜è®¤ SKU ID
     * ERP productId å¯¹åº” MDM itemId,默认使用第一个 SKU
     */
    private Long getDefaultSkuId(Long productId) {
        if (productId == null) {
            return null;
        }
        List<MdmItemSkuRespDTO> skuList = mdmItemSkuApi.getItemSkuListByItemId(productId).getCheckedData();
        if (CollUtil.isEmpty(skuList)) {
            return null;
        }
        return skuList.get(0).getId();
    }
}
yudao-module-erp/src/main/java/cn/iocoder/yudao/module/erp/service/stock/bo/ErpStockRecordCreateReqBO.java
@@ -1,6 +1,7 @@
package cn.iocoder.yudao.module.erp.service.stock.bo;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@@ -13,6 +14,7 @@
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class ErpStockRecordCreateReqBO {
    /**
@@ -54,9 +56,4 @@
    @NotNull(message = "业务单号不能为空")
    private String bizNo;
    /**
     * å¤‡æ³¨
     */
    private String remark;
}
}
yudao-module-im/pom.xml
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
<?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.boot</groupId>
        <artifactId>yudao</artifactId>
        <version>${revision}</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>yudao-module-im</artifactId>
    <packaging>jar</packaging>
    <name>${project.artifactId}</name>
    <description>
        im æ¨¡å—,我们放即时通讯业务。
        ä¾‹å¦‚说:单聊、群聊、消息收发、消息撤回、消息已读等等
    </description>
    <dependencies>
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-module-system</artifactId>
            <version>${revision}</version>
        </dependency>
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-module-infra</artifactId>
            <version>${revision}</version>
        </dependency>
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-common</artifactId>
        </dependency>
        <!-- Web ç›¸å…³ -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-spring-boot-starter-security</artifactId>
        </dependency>
        <!-- DB ç›¸å…³ -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-spring-boot-starter-mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-spring-boot-starter-redis</artifactId>
        </dependency>
        <!-- å·¥å…·ç±»ç›¸å…³ -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-spring-boot-starter-excel</artifactId>
        </dependency>
        <!-- æ•æ„Ÿè¯æ£€æµ‹ï¼šsensitive-word(trie æ ‘高效匹配,支持忽略大小写 / å…¨åŠè§’ / æ•°å­—风格),版本由 yudao-dependencies ç»Ÿä¸€ç®¡ç† -->
        <dependency>
            <groupId>com.github.houbb</groupId>
            <artifactId>sensitive-word</artifactId>
        </dependency>
        <!-- æ±‰å­—转拼音:作为 hutool PinyinUtil çš„底层引擎,业务侧调用 StrUtils.toPinyin æ—¶æŒ‰éœ€å¼•å…¥ -->
        <dependency>
            <groupId>com.belerweb</groupId>
            <artifactId>pinyin4j</artifactId>
        </dependency>
        <!-- Test æµ‹è¯•相关 -->
        <dependency>
            <groupId>cn.iocoder.boot</groupId>
            <artifactId>yudao-spring-boot-starter-test</artifactId>
        </dependency>
    </dependencies>
</project>
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/api/package-info.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,5 @@
/**
 * @author anhaohao
 * @since 2024/3/9 ä¸‹åˆ8:59
 */
package cn.iocoder.yudao.module.im.api;
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/channel/ImChannelMaterialController.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.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));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/ImConversationReadController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.im.controller.admin.conversation;
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.conversation.vo.ImConversationReadRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.conversation.ImConversationReadDO;
import cn.iocoder.yudao.module.im.service.conversation.ImConversationReadService;
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 jakarta.annotation.Resource;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
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 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/conversation-read")
@Validated
public class ImConversationReadController {
    @Resource
    private ImConversationReadService conversationReadService;
    @GetMapping("/pull")
    @Operation(summary = "增量拉取当前用户的会话读位置(重连 / ç¦»çº¿è¡¥å¿ï¼‰")
    @Parameters({
            @Parameter(name = "lastUpdateTime", description = "上次拉取到的最新更新时间(毫秒时间戳);首次拉取不传"),
            @Parameter(name = "lastId", description = "上次拉取到的最后一条记录 id;首次拉取不传"),
            @Parameter(name = "limit", description = "单次拉取条数", required = true)
    })
    public CommonResult<List<ImConversationReadRespVO>> pullMyConversationRead(
            @RequestParam(value = "lastUpdateTime", required = false) Long lastUpdateTime,
            @RequestParam(value = "lastId", required = false) Long lastId,
            @RequestParam("limit") @Min(1) @Max(200) Integer limit) {
        List<ImConversationReadDO> list = conversationReadService.pullConversationReadList(
                getLoginUserId(), lastUpdateTime, lastId, limit);
        return success(BeanUtils.toBean(list, ImConversationReadRespVO.class));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/conversation/vo/ImConversationReadRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.im.controller.admin.conversation.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 ImConversationReadRespVO {
    @Schema(description = "读位置编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long id;
    @Schema(description = "会话类型", example = "1")
    private Integer conversationType; // å‚见 ImConversationTypeEnum æžšä¸¾
    @Schema(description = "目标编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    private Long targetId;
    @Schema(description = "最大已读消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
    private Long messageId;
    @Schema(description = "最近更新时间(增量拉取游标用)")
    private LocalDateTime updateTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/ImFacePackController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.im.controller.admin.face;
import cn.hutool.core.collection.ListUtil;
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 jakarta.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(ListUtil.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(), ListUtil.of()), ImFacePackUserRespVO.Item.class));
            return vo;
        });
        return success(result);
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/ImFaceUserItemController.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.annotation.Resource;
import jakarta.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);
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/pack/ImFacePackUserRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -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;
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/userItem/ImFaceUserItemRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/face/vo/userItem/ImFaceUserItemSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.im.controller.admin.face.vo.useritem;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/ImFriendController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,143 @@
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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.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() {
        List<ImFriendDO> friends = friendService.getFriendList(getLoginUserId());
        return success(buildFriendRespVOList(friends));
    }
    @GetMapping("/pull")
    @Operation(summary = "增量拉取当前用户的好友关系(重连 / ç¦»çº¿è¡¥å¿ï¼‰")
    @Parameters({
            @Parameter(name = "lastUpdateTime", description = "上次拉取到的最新更新时间(毫秒时间戳);首次拉取不传"),
            @Parameter(name = "lastId", description = "上次拉取到的最后一条记录 id;首次拉取不传"),
            @Parameter(name = "limit", description = "单次拉取条数", required = true)
    })
    public CommonResult<List<ImFriendRespVO>> pullMyFriendList(
            @RequestParam(value = "lastUpdateTime", required = false) Long lastUpdateTime,
            @RequestParam(value = "lastId", required = false) Long lastId,
            @RequestParam("limit") @Min(1) @Max(200) Integer limit) {
        List<ImFriendDO> list = friendService.pullFriendList(getLoginUserId(), lastUpdateTime, lastId, limit);
        return success(buildFriendRespVOList(list));
    }
    @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(name = "friendUserId", description = "好友的用户编号", required = true, example = "2048"),
            @Parameter(name = "clear", 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)));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/ImFriendRequestController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,143 @@
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.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.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 åŠ è½½æ›´å¤šï¼‰")
    @Parameters({
            @Parameter(name = "maxId", description = "当前列表最旧记录的 id;首页不传"),
            @Parameter(name = "limit", description = "单次拉取条数", required = true)
    })
    public CommonResult<List<ImFriendRequestRespVO>> getMyFriendRequestList(
            @RequestParam(value = "maxId", required = false) Long maxId,
            @RequestParam("limit") @Min(1) @Max(200) Integer limit) {
        List<ImFriendRequestDO> list = friendRequestService.getMyFriendRequestList(getLoginUserId(), maxId, limit);
        return success(buildList(list));
    }
    @GetMapping("/pull")
    @Operation(summary = "增量拉取「我相关」的好友申请(重连 / ç¦»çº¿è¡¥å¿ï¼‰")
    @Parameters({
            @Parameter(name = "lastUpdateTime", description = "上次拉取到的最新更新时间(毫秒时间戳);首次拉取不传"),
            @Parameter(name = "lastId", description = "上次拉取到的最后一条记录 id;首次拉取不传"),
            @Parameter(name = "limit", description = "单次拉取条数", required = true)
    })
    public CommonResult<List<ImFriendRequestRespVO>> pullMyFriendRequestList(
            @RequestParam(value = "lastUpdateTime", required = false) Long lastUpdateTime,
            @RequestParam(value = "lastId", required = false) Long lastId,
            @RequestParam("limit") @Min(1) @Max(200) Integer limit) {
        List<ImFriendRequestDO> list = friendRequestService.pullFriendRequestList(getLoginUserId(), lastUpdateTime, lastId, 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;
        });
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/vo/ImFriendRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
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 = "最近更新时间(增量拉取游标用)")
    private LocalDateTime updateTime;
    // ========== ä¸‹é¢æ˜¯èšåˆå­—段,方便前端显示 ==========
    @Schema(description = "好友昵称(实时聚合自 AdminUser)", example = "芋道")
    private String nickname;
    @Schema(description = "好友昵称的拼音(小写无空格)", example = "yudao")
    private String nicknamePinyin;
    @Schema(description = "好友头像(实时聚合自 AdminUser)")
    private String avatar;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/vo/ImFriendUpdateReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.im.controller.admin.friend.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/vo/request/ImFriendRequestApplyReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.validation.constraints.NotNull;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/friend/vo/request/ImFriendRequestRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
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 = "最近更新时间(增量拉取游标用)")
    private LocalDateTime updateTime;
    // ========== ä¸‹é¢æ˜¯èšåˆå­—段,方便前端显示 ==========
    @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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,215 @@
package cn.iocoder.yudao.module.im.controller.admin.group;
import cn.hutool.core.collection.CollUtil;
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.*;
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 jakarta.annotation.Resource;
import jakarta.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();
        }
        // ä»…当前用户是有效成员的群才允许回填置顶消息
        Map<Long, ImGroupMemberDO> memberMap = convertMap(
                groupMemberService.getGroupMemberListByUserId(loginUserId), ImGroupMemberDO::getGroupId);
        Set<Long> activeGroupIds = convertSet(memberMap.values(), ImGroupMemberDO::getGroupId,
                member -> CommonStatusEnum.ENABLE.getStatus().equals(member.getStatus()));
        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 groupVO = BeanUtils.toBean(group, ImGroupRespVO.class);
            // æ ‡è®°ç™»å½•用户在该群的成员状态,供前端区分当前群与历史退群
            boolean joined = activeGroupIds.contains(group.getId());
            groupVO.setJoinStatus(joined ? CommonStatusEnum.ENABLE.getStatus() : CommonStatusEnum.DISABLE.getStatus());
            MapUtils.findAndThen(memberMap, group.getId(), member ->
                    groupVO.setGroupRemark(member.getGroupRemark()).setSilent(Boolean.TRUE.equals(member.getSilent())));
            if (!joined || CollUtil.isEmpty(group.getPinnedMessageIds())) {
                return groupVO;
            }
            // æŒ‰ pin é¡ºåºè¾“出,已被删除的消息(messageMap æ²¡å‘½ä¸­ï¼‰è·³è¿‡
            List<ImGroupMessageDO> pinnedMesages = convertList(group.getPinnedMessageIds(), messageMap::get);
            return groupVO.setPinnedMessages(BeanUtils.toBean(pinnedMesages, ImGroupMessageRespVO.class));
        });
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupMemberController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,140 @@
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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.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());
        if (user != null) {
            memberVO.setNickname(user.getNickname()).setAvatar(user.getAvatar());
        }
        hidePrivateFieldsIfNotSelf(memberVO, member.getUserId(), loginUserId);
        return success(memberVO);
    }
    @GetMapping("/list")
    @Operation(summary = "获得指定群的成员列表(按群全量拉取,作为前端本地成员 cache çš„完整基线)")
    @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. æ‰¹é‡èšåˆæ˜µç§° / å¤´åƒï¼Œå¹¶å¯¹éžæœ¬äººç½®ç©ºç§äººå­—段
        return success(buildGroupMemberRespVOList(members));
    }
    // ========== ç§æœ‰æ–¹æ³•:VO ç»„装 ==========
    /**
     * ç¾¤æˆå‘˜åˆ—表批量转 VO ï¼‹ å…³è”回填昵称 ï¼ å¤´åƒï¼Œå¹¶å¯¹éžæœ¬äººç½®ç©ºç§äººå­—段
     */
    private List<ImGroupMemberRespVO> buildGroupMemberRespVOList(List<ImGroupMemberDO> members) {
        if (CollUtil.isEmpty(members)) {
            return Collections.emptyList();
        }
        Long loginUserId = getLoginUserId();
        // æ‰¹é‡èšåˆ AdminUser ä¿¡æ¯ï¼ˆæ˜µç§° ï¼ å¤´åƒï¼‰ï¼Œé¿å… N+1
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertList(members, ImGroupMemberDO::getUserId));
        return convertList(members, member -> {
            ImGroupMemberRespVO vo = BeanUtils.toBean(member, ImGroupMemberRespVO.class);
            MapUtils.findAndThen(userMap, member.getUserId(), user ->
                    vo.setNickname(user.getNickname()).setAvatar(user.getAvatar()));
            hidePrivateFieldsIfNotSelf(vo, member.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);
        }
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/ImGroupRequestController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,174 @@
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.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import jakarta.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("/pull")
    @Operation(summary = "增量拉取「我管理的群」下的加群申请(重连 / ç¦»çº¿è¡¥å¿ï¼‰")
    @Parameters({
            @Parameter(name = "lastUpdateTime", description = "上次拉取到的最新更新时间(毫秒时间戳);首次拉取不传"),
            @Parameter(name = "lastId", description = "上次拉取到的最后一条记录 id;首次拉取不传"),
            @Parameter(name = "limit", description = "单次拉取条数", required = true)
    })
    public CommonResult<List<ImGroupRequestRespVO>> pullMyGroupRequestList(
            @RequestParam(value = "lastUpdateTime", required = false) Long lastUpdateTime,
            @RequestParam(value = "lastId", required = false) Long lastId,
            @RequestParam("limit") @Min(1) @Max(200) Integer limit) {
        List<ImGroupRequestDO> list = groupRequestService.pullGroupRequestList(
                getLoginUserId(), lastUpdateTime, lastId, limit);
        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;
        });
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupAdminAddReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - æ·»åŠ ç¾¤ç®¡ç†å‘˜ 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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupAdminRemoveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - æ’¤é”€ç¾¤ç®¡ç†å‘˜ 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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupCancelMuteMemberReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupCreateReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupMessagePinReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupMuteAllReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupMuteMemberReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,25 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Min;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,62 @@
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 = "当前登录用户在该群的成员状态:在群 / å·²é€€ç¾¤ï¼ˆåŽ†å²é€€ç¾¤ç¾¤ä»è¿”å›žï¼Œä¾›å‰ç«¯å±•ç¤ºç¦»çº¿æ¶ˆæ¯çš„ç¾¤å / å¤´åƒï¼‰")
    private Integer joinStatus; // å‚见 CommonStatusEnum æžšä¸¾
    @Schema(description = "当前登录用户对该群的备注")
    private String groupRemark;
    @Schema(description = "当前登录用户是否免打扰")
    private Boolean silent;
    @Schema(description = "群置顶消息列表,按 pin é¡ºåºï¼ˆæœ€å…ˆç½®é¡¶çš„在前);非该群有效成员时为空")
    private List<ImGroupMessageRespVO> pinnedMessages;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupTransferOwnerReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/ImGroupUpdateReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
import jakarta.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);
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberCreateReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberInviteReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - ç¾¤æˆå‘˜é‚€è¯· 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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberRemoveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - ç¾¤æˆå‘˜ç§»é™¤ 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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/member/ImGroupMemberUpdateReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.im.controller.admin.group.vo.member;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/request/ImGroupRequestApplyReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.validation.constraints.NotNull;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/group/vo/request/ImGroupRequestRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
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 = "申请更新时间;增量拉取游标")
    private LocalDateTime updateTime;
    // ========== ä¸‹é¢æ˜¯èšåˆå­—段,方便前端显示 ==========
    @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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,82 @@
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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@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() {
        List<ImChannelDO> list = channelService.getChannelListByStatus(CommonStatusEnum.ENABLE.getStatus());
        return success(BeanUtils.toBean(list, ImChannelRespVO.class));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/ImChannelMaterialManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import 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));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/channel/ImChannelPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 æžšä¸¾ç±»
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/channel/ImChannelRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/channel/ImChannelSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import jakarta.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 ç¦ç”¨ï¼‰
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/channel/vo/material/ImChannelMaterialSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackItemManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.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));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFacePackManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.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));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/ImFaceUserItemManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Map;
import 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);
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 æžšä¸¾ç±»
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/item/ImFacePackItemSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -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 jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.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 ç¦ç”¨ï¼‰
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.pack;
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;
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 ImFacePackPageReqVO extends PageParam {
    @Schema(description = "表情包名称,模糊匹配", example = "猫")
    private String name;
    @Schema(description = "状态", example = "0")
    @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»
    @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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.pack;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM è¡¨æƒ…包 Response VO")
@Data
public class ImFacePackRespVO {
    @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, 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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/pack/ImFacePackSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,36 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.pack;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - IM è¡¨æƒ…包新增 / ä¿®æ”¹ Request VO")
@Data
public class ImFacePackSaveReqVO {
    @Schema(description = "编号(修改时必填)", example = "1024")
    private Long id;
    @Schema(description = "表情包名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "猫主子")
    @NotBlank(message = "表情包名称不能为空")
    @Size(max = 64, message = "表情包名称长度不能超过 64")
    private String name;
    @Schema(description = "表情包图标", example = "https://cdn.example.com/face/pack/cat.png")
    @Size(max = 512, message = "图标长度不能超过 512")
    private String icon;
    @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 ç¦ç”¨ï¼‰
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/useritem/ImFaceUserItemManagerPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem;
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 ImFaceUserItemManagerPageReqVO extends PageParam {
    @Schema(description = "所属用户编号", example = "1024")
    private Long userId;
    @Schema(description = "表情名,模糊匹配", example = "狗")
    private String name;
    @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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/face/vo/useritem/ImFaceUserItemManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.face.vo.useritem;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM ç”¨æˆ·è¡¨æƒ… Response VO")
@Data
public class ImFaceUserItemManagerRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "4096")
    private Long id;
    @Schema(description = "所属用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long userId;
    @Schema(description = "所属用户昵称", example = "张三")
    private String userNickname;
    @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 = "渲染宽度(像素)", example = "200")
    private Integer width;
    @Schema(description = "渲染高度(像素)", example = "200")
    private Integer height;
    @Schema(description = "添加时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/ImFriendManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,65 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.friend;
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.friend.vo.ImFriendManagerPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendManagerRespVO;
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.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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.convertSetByFlatMap;
@Tag(name = "管理后台 - IM å¥½å‹ç®¡ç†")
@RestController
@RequestMapping("/im/manager/friend")
@Validated
public class ImFriendManagerController {
    @Resource
    private ImFriendService friendService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/page")
    @Operation(summary = "获得好友关系分页")
    @PreAuthorize("@ss.hasPermission('im:manager:friend:query')")
    public CommonResult<PageResult<ImFriendManagerRespVO>> getFriendPage(
            @Valid ImFriendManagerPageReqVO pageReqVO) {
        // 1. åˆ†é¡µæŸ¥è¯¢
        PageResult<ImFriendDO> pageResult = friendService.getFriendPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // 2.1 ä¸€æ¬¡æ€§æ‰¹é‡æŸ¥è¯¢ç”¨æˆ· + å¥½å‹çš„æ˜µç§°
        Set<Long> userIds = convertSetByFlatMap(pageResult.getList(),
                f -> Stream.of(f.getUserId(), f.getFriendUserId()));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        // 2.2 è½¬æ¢ä¸º VO,填充昵称
        return success(BeanUtils.toBean(pageResult, ImFriendManagerRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getUserId(),
                    user -> vo.setUserNickname(user.getNickname()));
            MapUtils.findAndThen(userMap, vo.getFriendUserId(),
                    user -> vo.setFriendNickname(user.getNickname()));
        }));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/ImFriendRequestManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,66 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.friend;
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.friend.vo.ImFriendRequestManagerPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo.ImFriendRequestManagerRespVO;
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.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
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.convertSetByFlatMap;
@Tag(name = "管理后台 - IM å¥½å‹ç”³è¯·ç®¡ç†")
@RestController
@RequestMapping("/im/manager/friend-request")
@Validated
public class ImFriendRequestManagerController {
    @Resource
    private ImFriendRequestService friendRequestService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/page")
    @Operation(summary = "获得好友申请分页")
    @PreAuthorize("@ss.hasPermission('im:manager:friend-request:query')")
    public CommonResult<PageResult<ImFriendRequestManagerRespVO>> getFriendRequestPage(
            @Valid ImFriendRequestManagerPageReqVO pageReqVO) {
        // 1. åˆ†é¡µæŸ¥è¯¢
        PageResult<ImFriendRequestDO> pageResult = friendRequestService.getFriendRequestPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // 2.1 ä¸€æ¬¡æ€§æ‰¹é‡æŸ¥è¯¢å‘èµ·æ–¹ + æŽ¥æ”¶æ–¹çš„æ˜µç§°
        Set<Long> userIds = convertSetByFlatMap(pageResult.getList(),
                request -> Stream.of(request.getFromUserId(), request.getToUserId()));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        // 2.2 è½¬æ¢ä¸º VO,填充昵称
        return success(BeanUtils.toBean(pageResult, ImFriendRequestManagerRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getFromUserId(),
                    user -> vo.setFromNickname(user.getNickname()));
            MapUtils.findAndThen(userMap, vo.getToUserId(),
                    user -> vo.setToNickname(user.getNickname()));
        }));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/vo/ImFriendManagerPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo;
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 ImFriendManagerPageReqVO extends PageParam {
    @Schema(description = "用户编号", example = "1024")
    private Long userId;
    @Schema(description = "好友用户编号", example = "2048")
    private Long friendUserId;
    @Schema(description = "好友状态", example = "0")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»
    @Schema(description = "是否免打扰", example = "false")
    private Boolean silent;
    @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[] addTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/vo/ImFriendManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM å¥½å‹å…³ç³» Response VO")
@Data
public class ImFriendManagerRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long userId;
    @Schema(description = "用户昵称", example = "张三")
    private String userNickname;
    @Schema(description = "好友用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    private Long friendUserId;
    @Schema(description = "好友昵称", example = "李四")
    private String friendNickname;
    @Schema(description = "好友展示备注")
    private String displayName;
    @Schema(description = "添加来源", example = "1")
    private Integer addSource; // å‚见 ImFriendAddSourceEnum æžšä¸¾ç±»
    @Schema(description = "是否免打扰", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
    private Boolean silent;
    @Schema(description = "是否置顶联系人", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
    private Boolean pinned;
    @Schema(description = "是否拉黑", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
    private Boolean blocked;
    @Schema(description = "好友状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»
    @Schema(description = "添加好友时间")
    private LocalDateTime addTime;
    @Schema(description = "删除好友时间")
    private LocalDateTime deleteTime;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/vo/ImFriendRequestManagerPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo;
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 ImFriendRequestManagerPageReqVO extends PageParam {
    @Schema(description = "发起方用户编号", example = "1024")
    private Long fromUserId;
    @Schema(description = "接收方用户编号", example = "2048")
    private Long toUserId;
    @Schema(description = "处理结果", example = "0")
    private Integer handleResult; // å‚见 ImFriendRequestHandleResultEnum æžšä¸¾ç±»
    @Schema(description = "添加来源", example = "1")
    private Integer addSource; // å‚见 ImFriendAddSourceEnum æžšä¸¾ç±»
    @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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/friend/vo/ImFriendRequestManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.friend.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM å¥½å‹ç”³è¯· Response VO")
@Data
public class ImFriendRequestManagerRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "发起方用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long fromUserId;
    @Schema(description = "发起方昵称", example = "张三")
    private String fromNickname;
    @Schema(description = "接收方用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    private Long toUserId;
    @Schema(description = "接收方昵称", example = "李四")
    private String toNickname;
    @Schema(description = "申请理由", example = "我是芋艿")
    private String applyContent;
    @Schema(description = "发起方对接收方的备注")
    private String displayName;
    @Schema(description = "添加来源", example = "1")
    private Integer addSource; // å‚见 ImFriendAddSourceEnum æžšä¸¾ç±»
    @Schema(description = "处理结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer handleResult; // å‚见 ImFriendRequestHandleResultEnum æžšä¸¾ç±»
    @Schema(description = "处理理由", example = "暂不通过")
    private String handleContent;
    @Schema(description = "处理时间")
    private LocalDateTime handleTime;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/ImGroupManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group;
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.group.vo.ImGroupManagerBanReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupManagerRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.service.group.ImGroupMemberService;
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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - IM ç¾¤èŠç®¡ç†")
@RestController
@RequestMapping("/im/manager/group")
@Validated
public class ImGroupManagerController {
    @Resource
    private ImGroupService groupService;
    @Resource
    private ImGroupMemberService groupMemberService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/page")
    @Operation(summary = "获得群分页")
    @PreAuthorize("@ss.hasPermission('im:manager:group:query')")
    public CommonResult<PageResult<ImGroupManagerRespVO>> getGroupPage(@Valid ImGroupManagerPageReqVO pageReqVO) {
        // 1. åˆ†é¡µæŸ¥è¯¢ç¾¤
        PageResult<ImGroupDO> pageResult = groupService.getGroupPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // 2.1 æ‰¹é‡æŸ¥è¯¢ç›¸å…³æ•°æ®
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(pageResult.getList(), ImGroupDO::getOwnerUserId));
        Map<Long, Long> memberCountMap = groupMemberService.getActiveMemberCountMap(
                convertSet(pageResult.getList(), ImGroupDO::getId));
        // 2.2 è½¬æ¢ä¸º VO,填充群主昵称、群成员数量
        return success(BeanUtils.toBean(pageResult, ImGroupManagerRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getOwnerUserId(),
                    user -> vo.setOwnerNickname(user.getNickname()));
            vo.setMemberCount(memberCountMap.getOrDefault(vo.getId(), 0L).intValue());
        }));
    }
    @GetMapping("/get")
    @Operation(summary = "获得群详情")
    @Parameter(name = "id", description = "群编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:group:query')")
    public CommonResult<ImGroupManagerRespVO> getGroup(@RequestParam("id") Long id) {
        ImGroupDO group = groupService.getGroup(id);
        return success(BeanUtils.toBean(group, ImGroupManagerRespVO.class));
    }
    @PutMapping("/ban")
    @Operation(summary = "封禁群")
    @PreAuthorize("@ss.hasPermission('im:manager:group:ban')")
    public CommonResult<Boolean> banGroup(@Valid @RequestBody ImGroupManagerBanReqVO reqVO) {
        groupService.banGroup(getLoginUserId(), reqVO);
        return success(true);
    }
    @PutMapping("/unban")
    @Operation(summary = "解封群")
    @Parameter(name = "id", description = "群编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:group:ban')")
    public CommonResult<Boolean> unbanGroup(@RequestParam("id") Long id) {
        groupService.unbanGroup(getLoginUserId(), id);
        return success(true);
    }
    @DeleteMapping("/dissolve")
    @Operation(summary = "解散群")
    @Parameter(name = "id", description = "群编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:group:dissolve')")
    public CommonResult<Boolean> dissolveGroup(@RequestParam("id") Long id) {
        groupService.dissolveGroupByManager(getLoginUserId(), id);
        return success(true);
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/ImGroupMemberManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,60 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group;
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.module.im.controller.admin.manager.group.vo.member.ImGroupMemberManagerRespVO;
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.tags.Tag;
import jakarta.annotation.Resource;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - IM ç¾¤æˆå‘˜ç®¡ç†")
@RestController
@RequestMapping("/im/manager/group/member")
@Validated
public class ImGroupMemberManagerController {
    @Resource
    private ImGroupMemberService groupMemberService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/list")
    @Operation(summary = "获得群成员列表(含已退群成员,由前端按需过滤)")
    @Parameter(name = "groupId", description = "群编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:group:query')")
    public CommonResult<List<ImGroupMemberManagerRespVO>> getGroupMemberList(@RequestParam("groupId") Long groupId) {
        // 1. æŸ¥è¯¢ç¾¤å…¨éƒ¨æˆå‘˜ï¼ˆå«å·²é€€ç¾¤ï¼‰
        List<ImGroupMemberDO> members = groupMemberService.getGroupMemberListByGroupId(groupId);
        if (CollUtil.isEmpty(members)) {
            return success(Collections.emptyList());
        }
        // 2.1 æ‰¹é‡æŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(members, ImGroupMemberDO::getUserId));
        // 2.2 è½¬æ¢ä¸º VO,填充昵称、头像
        return success(BeanUtils.toBean(members, ImGroupMemberManagerRespVO.class, vo ->
                MapUtils.findAndThen(userMap, vo.getUserId(), user ->
                        vo.setNickname(user.getNickname()).setAvatar(user.getAvatar()))));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/ImGroupRequestManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group;
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.group.vo.ImGroupRequestManagerPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.ImGroupRequestManagerRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupRequestDO;
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.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.Objects;
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.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
@Tag(name = "管理后台 - IM åŠ ç¾¤ç”³è¯·ç®¡ç†")
@RestController
@RequestMapping("/im/manager/group-request")
@Validated
public class ImGroupRequestManagerController {
    @Resource
    private ImGroupRequestService groupRequestService;
    @Resource
    private ImGroupService groupService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/page")
    @Operation(summary = "获得加群申请分页")
    @PreAuthorize("@ss.hasPermission('im:manager:group-request:query')")
    public CommonResult<PageResult<ImGroupRequestManagerRespVO>> getGroupRequestPage(
            @Valid ImGroupRequestManagerPageReqVO pageReqVO) {
        // 1. åˆ†é¡µæŸ¥è¯¢
        PageResult<ImGroupRequestDO> pageResult = groupRequestService.getGroupRequestPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // 2.1 æ‰¹é‡èšåˆ user / inviter / handler ç”¨æˆ·æ˜µç§°
        Set<Long> userIds = convertSetByFlatMap(pageResult.getList(),
                request -> Stream.of(request.getUserId(), request.getInviterUserId(), request.getHandleUserId())
                        .filter(Objects::nonNull));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        // 2.2 æ‰¹é‡èšåˆç¾¤ä¿¡æ¯ï¼ˆå–群名)
        Set<Long> groupIds = convertSet(pageResult.getList(), ImGroupRequestDO::getGroupId);
        Map<Long, ImGroupDO> groupMap = groupService.getGroupMap(groupIds);
        return success(BeanUtils.toBean(pageResult, ImGroupRequestManagerRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getUserId(), user -> vo.setUserNickname(user.getNickname()));
            MapUtils.findAndThen(userMap, vo.getInviterUserId(), user -> vo.setInviterNickname(user.getNickname()));
            MapUtils.findAndThen(userMap, vo.getHandleUserId(), user -> vo.setHandleNickname(user.getNickname()));
            MapUtils.findAndThen(groupMap, vo.getGroupId(), group -> vo.setGroupName(group.getName()));
        }));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupManagerBanReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,22 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - IM ç¾¤èŠå°ç¦ Request VO")
@Data
public class ImGroupManagerBanReqVO {
    @Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @NotNull(message = "群编号不能为空")
    private Long id;
    @Schema(description = "封禁原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "违规内容")
    @NotBlank(message = "封禁原因不能为空")
    @Size(max = 200, message = "封禁原因长度不能超过 200")
    private String reason;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupManagerPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,34 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group.vo;
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 ImGroupManagerPageReqVO extends PageParam {
    @Schema(description = "群名称,模糊匹配", example = "技术交流群")
    private String name;
    @Schema(description = "群主用户编号", example = "1024")
    private Long ownerUserId;
    @Schema(description = "群状态", example = "0")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»ï¼ˆ0 æ­£å¸¸ / 1 å·²è§£æ•£ï¼‰
    @Schema(description = "是否封禁", example = "false")
    private Boolean banned;
    @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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM ç¾¤èŠ Response VO")
@Data
public class ImGroupManagerRespVO {
    @Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "群名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "技术交流群")
    private String name;
    @Schema(description = "群头像")
    private String avatar;
    @Schema(description = "群公告")
    private String notice;
    @Schema(description = "群主用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long ownerUserId;
    @Schema(description = "群主昵称", example = "张三")
    private String ownerNickname;
    @Schema(description = "群成员数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "12")
    private Integer memberCount;
    @Schema(description = "群状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»
    @Schema(description = "解散时间")
    private LocalDateTime dissolvedTime;
    @Schema(description = "是否封禁", requiredMode = Schema.RequiredMode.REQUIRED, example = "false")
    private Boolean banned;
    @Schema(description = "是否全群禁言")
    private Boolean mutedAll;
    @Schema(description = "封禁原因")
    private String bannedReason;
    @Schema(description = "封禁时间")
    private LocalDateTime bannedTime;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupRequestManagerPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group.vo;
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 ImGroupRequestManagerPageReqVO extends PageParam {
    @Schema(description = "群编号", example = "1024")
    private Long groupId;
    @Schema(description = "申请人 / è¢«é‚€è¯·äººç”¨æˆ·ç¼–号", example = "2048")
    private Long userId;
    @Schema(description = "邀请人用户编号", example = "31460")
    private Long inviterUserId;
    @Schema(description = "处理结果", example = "0")
    private Integer handleResult; // å‚见 ImGroupRequestHandleResultEnum æžšä¸¾ç±»
    @Schema(description = "加入来源", example = "1")
    private Integer addSource; // å‚见 ImGroupAddSourceEnum æžšä¸¾ç±»
    @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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/ImGroupRequestManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM åŠ ç¾¤ç”³è¯· Response VO")
@Data
public class ImGroupRequestManagerRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long groupId;
    @Schema(description = "群名称", example = "芋道技术交流群")
    private String groupName;
    @Schema(description = "申请人 / è¢«é‚€è¯·äººç”¨æˆ·ç¼–号", requiredMode = Schema.RequiredMode.REQUIRED, example = "100")
    private Long userId;
    @Schema(description = "申请人 / è¢«é‚€è¯·äººæ˜µç§°", example = "张三")
    private String userNickname;
    @Schema(description = "邀请人用户编号;NULL è¡¨ç¤ºç”¨æˆ·ä¸»åŠ¨ç”³è¯·", example = "200")
    private Long inviterUserId;
    @Schema(description = "邀请人昵称", example = "老张")
    private String inviterNickname;
    @Schema(description = "申请理由", example = "我想加入这个群")
    private String applyContent;
    @Schema(description = "加入来源", example = "1")
    private Integer addSource; // å‚见 ImGroupAddSourceEnum æžšä¸¾ç±»
    @Schema(description = "处理结果", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer handleResult; // å‚见 ImGroupRequestHandleResultEnum æžšä¸¾ç±»
    @Schema(description = "处理人用户编号", example = "31460")
    private Long handleUserId;
    @Schema(description = "处理人昵称", example = "管理员")
    private String handleNickname;
    @Schema(description = "处理理由", example = "暂不通过")
    private String handleContent;
    @Schema(description = "处理时间")
    private LocalDateTime handleTime;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/group/vo/member/ImGroupMemberManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.group.vo.member;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM ç¾¤æˆå‘˜ Response VO")
@Data
public class ImGroupMemberManagerRespVO {
    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long userId;
    @Schema(description = "用户昵称", example = "张三")
    private String nickname;
    @Schema(description = "用户头像")
    private String avatar;
    @Schema(description = "组内显示名", example = "三哥")
    private String displayUserName;
    @Schema(description = "群备注", example = "技术交流群")
    private String groupRemark;
    @Schema(description = "是否免打扰", example = "false")
    private Boolean silent;
    @Schema(description = "成员状态", example = "0")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»
    @Schema(description = "成员角色;1=群主 2=管理员 3=普通成员", example = "3")
    private Integer role; // å‚见 ImGroupMemberRoleEnum æžšä¸¾ç±»
    @Schema(description = "入群时间")
    private LocalDateTime joinTime;
    @Schema(description = "退群时间")
    private LocalDateTime quitTime;
    @Schema(description = "禁言到期时间")
    private LocalDateTime muteEndTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImChannelMessageManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,80 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message;
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.message.vo.channel.ImChannelMessagePageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessageRespVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel.ImChannelMessageSendReqVO;
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.dal.dataobject.message.ImChannelMessageDO;
import cn.iocoder.yudao.module.im.service.channel.ImChannelMaterialService;
import cn.iocoder.yudao.module.im.service.message.ImChannelMessageService;
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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - IM é¢‘道消息")
@RestController
@RequestMapping("/im/manager/channel-message")
@Validated
public class ImChannelMessageManagerController {
    @Resource
    private ImChannelMessageService channelMessageService;
    @Resource
    private ImChannelService channelService;
    @Resource
    private ImChannelMaterialService channelMaterialService;
    @PostMapping("/send")
    @Operation(summary = "立即推送频道消息")
    @PreAuthorize("@ss.hasPermission('im:manager:channel-message:send')")
    public CommonResult<Long> sendMessage(@Valid @RequestBody ImChannelMessageSendReqVO reqVO) {
        return success(channelMessageService.sendMessage(reqVO));
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除频道消息")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:channel-message:delete')")
    public CommonResult<Boolean> deleteMessage(@RequestParam("id") Long id) {
        channelMessageService.deleteMessage(id);
        return success(true);
    }
    @GetMapping("/page")
    @Operation(summary = "获得频道消息分页;回填频道名 / ç´ ææ ‡é¢˜")
    @PreAuthorize("@ss.hasPermission('im:manager:channel-message:query')")
    public CommonResult<PageResult<ImChannelMessageRespVO>> getMessagePage(@Valid ImChannelMessagePageReqVO pageReqVO) {
        PageResult<ImChannelMessageDO> pageResult = channelMessageService.getMessagePage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // æ‰¹é‡æŸ¥è¯¢é¢‘道和素材,并回填频道名 / ç´ ææ ‡é¢˜
        Map<Long, ImChannelDO> channelMap = channelService.getChannelMap(
                convertSet(pageResult.getList(), ImChannelMessageDO::getChannelId));
        Map<Long, ImChannelMaterialDO> materialMap = channelMaterialService.getMaterialMap(
                convertSet(pageResult.getList(), ImChannelMessageDO::getMaterialId));
        return success(BeanUtils.toBean(pageResult, ImChannelMessageRespVO.class, vo -> {
            MapUtils.findAndThen(channelMap, vo.getChannelId(), c -> vo.setChannelName(c.getName()));
            MapUtils.findAndThen(materialMap, vo.getMaterialId(),
                    material -> vo.setMaterialTitle(material.getTitle()).setMaterialCoverUrl(material.getCoverUrl()));
        }));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImGroupMessageManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,92 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message;
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.message.vo.group.ImGroupMessageManagerPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group.ImGroupMessageManagerRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO;
import cn.iocoder.yudao.module.im.service.group.ImGroupService;
import cn.iocoder.yudao.module.im.service.message.ImGroupMessageService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
import java.util.Objects;
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.convertSet;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSetByFlatMap;
import static cn.iocoder.yudao.module.im.enums.ImCommonConstants.AT_USER_ID_ALL;
@Tag(name = "管理后台 - IM ç¾¤èŠæ¶ˆæ¯")
@RestController
@RequestMapping("/im/manager/message/group")
@Validated
public class ImGroupMessageManagerController {
    @Resource
    private ImGroupMessageService groupMessageService;
    @Resource
    private ImGroupService groupService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/page")
    @Operation(summary = "获得群聊消息分页")
    @PreAuthorize("@ss.hasPermission('im:manager:message:query')")
    public CommonResult<PageResult<ImGroupMessageManagerRespVO>> getGroupMessagePage(
            @Valid ImGroupMessageManagerPageReqVO pageReqVO) {
        // 1. åˆ†é¡µæŸ¥è¯¢
        PageResult<ImGroupMessageDO> pageResult = groupMessageService.getGroupMessagePage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // 2.1 æ‰¹é‡æŸ¥è¯¢ç¾¤åç§°ã€å‘送人昵称、@ ç”¨æˆ·æ˜µç§°ï¼ˆ-1 è¡¨ç¤º @所有人,跳过查询,由前端判断渲染)
        Map<Long, ImGroupDO> groupMap = groupService.getGroupMap(
                convertSet(pageResult.getList(), ImGroupMessageDO::getGroupId));
        Set<Long> userIds = convertSetByFlatMap(pageResult.getList(), m -> Stream.concat(
                Stream.of(m.getSenderId()),
                CollUtil.emptyIfNull(m.getAtUserIds()).stream()
                        .filter(id -> !Objects.equals(id, AT_USER_ID_ALL))));
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(userIds);
        // 2.2 è½¬æ¢ä¸º VO,填充群名 / å‘送人昵称 / @ ç”¨æˆ·æ˜µç§°ï¼ˆ-1 ä½ç½®ç•™ null,由前端展示「@所有人」)
        return success(BeanUtils.toBean(pageResult, ImGroupMessageManagerRespVO.class, vo -> {
            MapUtils.findAndThen(groupMap, vo.getGroupId(), group -> vo.setGroupName(group.getName()));
            MapUtils.findAndThen(userMap, vo.getSenderId(), user -> vo.setSenderNickname(user.getNickname()));
            if (CollUtil.isNotEmpty(vo.getAtUserIds())) {
                vo.setAtUserNicknames(convertList(vo.getAtUserIds(), id -> {
                    AdminUserRespDTO user = userMap.get(id);
                    return user != null ? user.getNickname() : null;
                }));
            }
        }));
    }
    @GetMapping("/get")
    @Operation(summary = "获得群聊消息详情")
    @Parameter(name = "id", description = "消息编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:message:query')")
    public CommonResult<ImGroupMessageManagerRespVO> getGroupMessage(@RequestParam("id") Long id) {
        ImGroupMessageDO message = groupMessageService.getGroupMessage(id);
        return success(BeanUtils.toBean(message, ImGroupMessageManagerRespVO.class));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/ImPrivateMessageManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,72 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message;
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.message.vo.privates.ImPrivateMessageManagerPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates.ImPrivateMessageManagerRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO;
import cn.iocoder.yudao.module.im.service.message.ImPrivateMessageService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
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.convertSetByFlatMap;
@Tag(name = "管理后台 - IM ç§èŠæ¶ˆæ¯")
@RestController
@RequestMapping("/im/manager/message/private")
@Validated
public class ImPrivateMessageManagerController {
    @Resource
    private ImPrivateMessageService privateMessageService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/page")
    @Operation(summary = "获得私聊消息分页")
    @PreAuthorize("@ss.hasPermission('im:manager:message:query')")
    public CommonResult<PageResult<ImPrivateMessageManagerRespVO>> getPrivateMessagePage(
            @Valid ImPrivateMessageManagerPageReqVO pageReqVO) {
        // 1. åˆ†é¡µæŸ¥è¯¢
        PageResult<ImPrivateMessageDO> pageResult = privateMessageService.getPrivateMessagePage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // 2.1 ä¸€æ¬¡æ€§æ‰¹é‡æŸ¥è¯¢å‘送人 + æŽ¥æ”¶äººæ˜µç§°
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSetByFlatMap(pageResult.getList(),
                m -> Stream.of(m.getSenderId(), m.getReceiverId())));
        // 2.2 è½¬æ¢ä¸º VO,填充昵称
        return success(BeanUtils.toBean(pageResult, ImPrivateMessageManagerRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getSenderId(), user -> vo.setSenderNickname(user.getNickname()));
            MapUtils.findAndThen(userMap, vo.getReceiverId(), user -> vo.setReceiverNickname(user.getNickname()));
        }));
    }
    @GetMapping("/get")
    @Operation(summary = "获得私聊消息详情")
    @Parameter(name = "id", description = "消息编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:message:query')")
    public CommonResult<ImPrivateMessageManagerRespVO> getPrivateMessage(@RequestParam("id") Long id) {
        ImPrivateMessageDO message = privateMessageService.getPrivateMessage(id);
        return success(BeanUtils.toBean(message, ImPrivateMessageManagerRespVO.class));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessagePageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel;
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 ImChannelMessagePageReqVO extends PageParam {
    @Schema(description = "频道编号", example = "1")
    private Long channelId;
    @Schema(description = "素材编号", example = "1024")
    private Long materialId;
    @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[] sendTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - IM é¢‘道消息 Response VO")
@Data
public class ImChannelMessageRespVO {
    @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 = "1024")
    private Long materialId;
    @Schema(description = "素材标题(关联查询填充)")
    private String materialTitle;
    @Schema(description = "素材封面 URL(关联查询填充)")
    private String materialCoverUrl;
    @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "125")
    private Integer type; // å‚见 ImContentTypeEnum æžšä¸¾ç±»
    @Schema(description = "消息内容;payload JSON å¿«ç…§")
    private String content;
    @Schema(description = "接收人编号列表;为空表示全员")
    private List<Long> receiverUserIds;
    @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime sendTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/channel/ImChannelMessageSendReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,20 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.channel;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
@Schema(description = "管理后台 - IM é¢‘道消息推送 Request VO")
@Data
public class ImChannelMessageSendReqVO {
    @Schema(description = "素材编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    @NotNull(message = "素材编号不能为空")
    private Long materialId;
    @Schema(description = "接收人编号列表;为空表示全员", example = "[1024, 2048]")
    private List<Long> receiverUserIds;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/group/ImGroupMessageManagerPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group;
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 ImGroupMessageManagerPageReqVO extends PageParam {
    @Schema(description = "群编号", example = "1024")
    private Long groupId;
    @Schema(description = "发送人编号", example = "1024")
    private Long senderId;
    @Schema(description = "消息类型", example = "1")
    private Integer type; // å‚见 ImContentTypeEnum æžšä¸¾ç±»
    @Schema(description = "消息内容", example = "你好")
    private String content;
    @Schema(description = "消息状态", example = "0")
    private Integer status; // å‚见 ImMessageStatusEnum æžšä¸¾ç±»
    @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[] sendTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/group/ImGroupMessageManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.group;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
@Schema(description = "管理后台 - IM ç¾¤èŠæ¶ˆæ¯ Response VO")
@Data
public class ImGroupMessageManagerRespVO {
    @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "客户端消息编号", example = "c-uuid-xxx")
    private String clientMessageId;
    @Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long groupId;
    @Schema(description = "群名称", example = "技术交流群")
    private String groupName;
    @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long senderId;
    @Schema(description = "发送人昵称", example = "张三")
    private String senderNickname;
    @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Integer type; // å‚见 ImContentTypeEnum æžšä¸¾ç±»
    @Schema(description = "消息内容(JSON æ ¼å¼ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED)
    private String content;
    @Schema(description = "消息状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer status; // å‚见 ImMessageStatusEnum æžšä¸¾ç±»
    @Schema(description = "@ ç›®æ ‡ç”¨æˆ·ç¼–号列表(-1 è¡¨ç¤º @所有人)")
    private List<Long> atUserIds;
    @Schema(description = "@ ç›®æ ‡ç”¨æˆ·æ˜µç§°åˆ—表(-1 ä½ç½®ä¸º null,前端根据 atUserIds è‡ªè¡Œå±•示「@所有人」)")
    private List<String> atUserNicknames;
    @Schema(description = "回执状态", example = "0")
    private Integer receiptStatus; // å‚见 ImMessageReceiptStatusEnum æžšä¸¾ç±»
    @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime sendTime;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/privates/ImPrivateMessageManagerPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates;
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 ImPrivateMessageManagerPageReqVO extends PageParam {
    @Schema(description = "发送人编号", example = "1024")
    private Long senderId;
    @Schema(description = "接收人编号", example = "2048")
    private Long receiverId;
    @Schema(description = "消息类型", example = "1")
    private Integer type; // å‚见 ImContentTypeEnum æžšä¸¾ç±»
    @Schema(description = "消息内容", example = "你好")
    private String content;
    @Schema(description = "消息状态", example = "0")
    private Integer status; // å‚见 ImMessageStatusEnum æžšä¸¾ç±»
    @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[] sendTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/message/vo/privates/ImPrivateMessageManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.message.vo.privates;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM ç§èŠæ¶ˆæ¯ Response VO")
@Data
public class ImPrivateMessageManagerRespVO {
    @Schema(description = "消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "客户端消息编号", example = "c-uuid-xxx")
    private String clientMessageId;
    @Schema(description = "发送人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long senderId;
    @Schema(description = "发送人昵称", example = "张三")
    private String senderNickname;
    @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    private Long receiverId;
    @Schema(description = "接收人昵称", example = "李四")
    private String receiverNickname;
    @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Integer type; // å‚见 ImContentTypeEnum æžšä¸¾ç±»
    @Schema(description = "消息内容(JSON æ ¼å¼ï¼‰", requiredMode = Schema.RequiredMode.REQUIRED)
    private String content;
    @Schema(description = "消息状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer status; // å‚见 ImMessageStatusEnum æžšä¸¾ç±»
    @Schema(description = "回执状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer receiptStatus; // å‚见 ImMessageReceiptStatusEnum æžšä¸¾ç±»
    @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime sendTime;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/rtc/ImRtcCallManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,113 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.rtc;
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.rtc.vo.ImRtcCallManagerPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.rtc.vo.ImRtcCallManagerRespVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.rtc.vo.ImRtcParticipantManagerRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.dal.dataobject.rtc.ImRtcCallDO;
import cn.iocoder.yudao.module.im.dal.dataobject.rtc.ImRtcParticipantDO;
import cn.iocoder.yudao.module.im.service.group.ImGroupService;
import cn.iocoder.yudao.module.im.service.rtc.ImRtcCallService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - IM é€šè¯è®°å½•")
@RestController
@RequestMapping("/im/manager/rtc")
@Validated
public class ImRtcCallManagerController {
    @Resource
    private ImRtcCallService rtcCallService;
    @Resource
    private ImGroupService groupService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/page")
    @Operation(summary = "获得通话记录分页")
    @PreAuthorize("@ss.hasPermission('im:manager:rtc:query')")
    public CommonResult<PageResult<ImRtcCallManagerRespVO>> getCallPage(@Valid ImRtcCallManagerPageReqVO pageReqVO) {
        PageResult<ImRtcCallDO> pageResult = rtcCallService.getCallPage(pageReqVO);
        return success(buildCallRespVOPage(pageResult));
    }
    @GetMapping("/get")
    @Operation(summary = "获得通话记录详情")
    @Parameter(name = "id", description = "通话编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:rtc:query')")
    public CommonResult<ImRtcCallManagerRespVO> getCall(@RequestParam("id") Long id) {
        ImRtcCallDO call = rtcCallService.getCall(id);
        if (call == null) {
            return success(null);
        }
        return success(CollUtil.getFirst(buildCallRespVOList(Collections.singletonList(call))));
    }
    @GetMapping("/participant-list")
    @Operation(summary = "获得通话参与者列表")
    @Parameter(name = "id", description = "通话编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:rtc:query')")
    public CommonResult<List<ImRtcParticipantManagerRespVO>> getCallParticipantList(@RequestParam("id") Long id) {
        List<ImRtcParticipantDO> participants = rtcCallService.getCallParticipantListByCallId(id);
        if (CollUtil.isEmpty(participants)) {
            return success(Collections.emptyList());
        }
        // æŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(participants, ImRtcParticipantDO::getUserId));
        // ç»„装返回
        return success(BeanUtils.toBean(participants, ImRtcParticipantManagerRespVO.class, vo ->
                MapUtils.findAndThen(userMap, vo.getUserId(),
                        user -> vo.setUserNickname(user.getNickname()))));
    }
    // ========== ç§æœ‰æ–¹æ³•:VO ç»„装 ==========
    private PageResult<ImRtcCallManagerRespVO> buildCallRespVOPage(PageResult<ImRtcCallDO> pageResult) {
        if (CollUtil.isEmpty(pageResult.getList())) {
            return PageResult.empty(pageResult.getTotal());
        }
        return new PageResult<>(buildCallRespVOList(pageResult.getList()), pageResult.getTotal());
    }
    private List<ImRtcCallManagerRespVO> buildCallRespVOList(List<ImRtcCallDO> calls) {
        // æŸ¥è¯¢ç”¨æˆ·ä¿¡æ¯
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(
                convertSet(calls, ImRtcCallDO::getInviterUserId));
        Map<Long, ImGroupDO> groupMap = groupService.getGroupMap(
                convertSet(calls, ImRtcCallDO::getGroupId));
        // ç»„装返回
        return BeanUtils.toBean(calls, ImRtcCallManagerRespVO.class, vo -> {
            MapUtils.findAndThen(userMap, vo.getInviterUserId(),
                    user -> vo.setInviterNickname(user.getNickname()));
            MapUtils.findAndThen(groupMap, vo.getGroupId(),
                    group -> vo.setGroupName(group.getName()));
        });
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/rtc/vo/ImRtcCallManagerPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.rtc.vo;
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 ImRtcCallManagerPageReqVO extends PageParam {
    @Schema(description = "发起人用户编号", example = "1024")
    private Long inviterUserId;
    @Schema(description = "会话类型", example = "1")
    private Integer conversationType; // å‚见 ImConversationTypeEnum æžšä¸¾ç±»
    @Schema(description = "媒体类型", example = "1")
    private Integer mediaType; // å‚见 ImRtcCallMediaTypeEnum æžšä¸¾ç±»
    @Schema(description = "通话状态", example = "10")
    private Integer status; // å‚见 ImRtcCallStatusEnum æžšä¸¾ç±»
    @Schema(description = "结束原因", example = "1")
    private Integer endReason; // å‚见 ImRtcCallEndReasonEnum æžšä¸¾ç±»
    @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[] startTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/rtc/vo/ImRtcCallManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,54 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.rtc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM é€šè¯è®°å½• Response VO")
@Data
public class ImRtcCallManagerRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "业务通话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "uuid-xxx")
    private String room;
    @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Integer conversationType; // å‚见 ImConversationTypeEnum æžšä¸¾ç±»
    @Schema(description = "媒体类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Integer mediaType; // å‚见 ImRtcCallMediaTypeEnum æžšä¸¾ç±»
    @Schema(description = "发起人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long inviterUserId;
    @Schema(description = "发起人昵称", example = "张三")
    private String inviterNickname;
    @Schema(description = "群编号;私聊为空", example = "999")
    private Long groupId;
    @Schema(description = "群名称;私聊为空", example = "测试群")
    private String groupName;
    @Schema(description = "通话状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
    private Integer status; // å‚见 ImRtcCallStatusEnum æžšä¸¾ç±»
    @Schema(description = "结束原因", example = "1")
    private Integer endReason; // å‚见 ImRtcCallEndReasonEnum æžšä¸¾ç±»
    @Schema(description = "发起时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime startTime;
    @Schema(description = "接通时间;未接通为空")
    private LocalDateTime acceptTime;
    @Schema(description = "结束时间;未结束为空")
    private LocalDateTime endTime;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/rtc/vo/ImRtcParticipantManagerRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.rtc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM é€šè¯å‚与者 Response VO")
@Data
public class ImRtcParticipantManagerRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "通话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2048")
    private Long callId;
    @Schema(description = "参与者用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long userId;
    @Schema(description = "参与者昵称", example = "张三")
    private String userNickname;
    @Schema(description = "参与角色", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Integer role; // å‚见 ImRtcParticipantRoleEnum æžšä¸¾ç±»
    @Schema(description = "参与状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
    private Integer status; // å‚见 ImRtcParticipantStatusEnum æžšä¸¾ç±»
    @Schema(description = "被邀请时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime inviteTime;
    @Schema(description = "接听时间;未接听为空")
    private LocalDateTime acceptTime;
    @Schema(description = "离开时间;未加入为空")
    private LocalDateTime leaveTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/ImSensitiveWordManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,106 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword;
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.number.NumberUtils;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordPageReqVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordRespVO;
import cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo.ImSensitiveWordSaveReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.sensitiveword.ImSensitiveWordDO;
import cn.iocoder.yudao.module.im.service.sensitiveword.ImSensitiveWordService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.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 java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
@Tag(name = "管理后台 - IM æ•æ„Ÿè¯")
@RestController
@RequestMapping("/im/manager/sensitive-word")
@Validated
public class ImSensitiveWordManagerController {
    @Resource
    private ImSensitiveWordService sensitiveWordService;
    @Resource
    private AdminUserApi adminUserApi;
    @PostMapping("/create")
    @Operation(summary = "新增敏感词")
    @PreAuthorize("@ss.hasPermission('im:manager:sensitive-word:create')")
    public CommonResult<Long> createSensitiveWord(@Valid @RequestBody ImSensitiveWordSaveReqVO reqVO) {
        return success(sensitiveWordService.createSensitiveWord(reqVO));
    }
    @PutMapping("/update")
    @Operation(summary = "修改敏感词")
    @PreAuthorize("@ss.hasPermission('im:manager:sensitive-word:update')")
    public CommonResult<Boolean> updateSensitiveWord(@Valid @RequestBody ImSensitiveWordSaveReqVO reqVO) {
        sensitiveWordService.updateSensitiveWord(reqVO);
        return success(true);
    }
    @DeleteMapping("/delete")
    @Operation(summary = "删除敏感词")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:sensitive-word:delete')")
    public CommonResult<Boolean> deleteSensitiveWord(@RequestParam("id") Long id) {
        sensitiveWordService.deleteSensitiveWord(id);
        return success(true);
    }
    @DeleteMapping("/delete-list")
    @Operation(summary = "批量删除敏感词")
    @Parameter(name = "ids", description = "编号列表", required = true)
    @PreAuthorize("@ss.hasPermission('im:manager:sensitive-word:delete')")
    public CommonResult<Boolean> deleteSensitiveWordList(
            @RequestParam("ids")
            @Size(max = 100, message = "批量删除最多 100 æ¡") List<Long> ids) {
        sensitiveWordService.deleteSensitiveWordList(ids);
        return success(true);
    }
    @GetMapping("/page")
    @Operation(summary = "获得敏感词分页")
    @PreAuthorize("@ss.hasPermission('im:manager:sensitive-word:query')")
    public CommonResult<PageResult<ImSensitiveWordRespVO>> getSensitiveWordPage(
            @Valid ImSensitiveWordPageReqVO pageReqVO) {
        // 1. åˆ†é¡µæŸ¥è¯¢
        PageResult<ImSensitiveWordDO> pageResult = sensitiveWordService.getSensitiveWordPage(pageReqVO);
        if (CollUtil.isEmpty(pageResult.getList())) {
            return success(PageResult.empty(pageResult.getTotal()));
        }
        // 2.1 æ‰¹é‡æŸ¥è¯¢åˆ›å»ºäººæ˜µç§°
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(convertSet(pageResult.getList(),
                word -> NumberUtils.parseLong(word.getCreator())));
        // 2.2 è½¬æ¢ä¸º VO,填充创建人昵称
        return success(BeanUtils.toBean(pageResult, ImSensitiveWordRespVO.class, vo ->
                MapUtils.findAndThen(userMap, NumberUtils.parseLong(vo.getCreator()),
                        user -> vo.setCreatorName(user.getNickname()))));
    }
    @GetMapping("/get")
    @Operation(summary = "获得敏感词详情")
    @Parameter(name = "id", description = "编号", required = true, example = "1024")
    @PreAuthorize("@ss.hasPermission('im:manager:sensitive-word:query')")
    public CommonResult<ImSensitiveWordRespVO> getSensitiveWord(@RequestParam("id") Long id) {
        ImSensitiveWordDO word = sensitiveWordService.getSensitiveWord(id);
        return success(BeanUtils.toBean(word, ImSensitiveWordRespVO.class));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordPageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,31 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo;
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;
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 ImSensitiveWordPageReqVO extends PageParam {
    @Schema(description = "敏感词,模糊匹配", example = "敏感")
    private String word;
    @Schema(description = "状态", example = "0")
    @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»ï¼ˆ0 å¯ç”¨ / 1 ç¦ç”¨ï¼‰
    @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;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "管理后台 - IM æ•æ„Ÿè¯ Response VO")
@Data
public class ImSensitiveWordRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long id;
    @Schema(description = "敏感词", requiredMode = Schema.RequiredMode.REQUIRED, example = "敏感词内容")
    private String word;
    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»
    @Schema(description = "创建人")
    private String creator;
    @Schema(description = "创建人昵称", example = "张三")
    private String creatorName;
    @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime createTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/sensitiveword/vo/ImSensitiveWordSaveReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.sensitiveword.vo;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Size;
import lombok.Data;
@Schema(description = "管理后台 - IM æ•æ„Ÿè¯æ–°å¢ž / ä¿®æ”¹ Request VO")
@Data
public class ImSensitiveWordSaveReqVO {
    @Schema(description = "编号(修改时必填)", example = "1024")
    private Long id;
    @Schema(description = "敏感词", requiredMode = Schema.RequiredMode.REQUIRED, example = "敏感词内容")
    @NotBlank(message = "敏感词不能为空")
    @Size(max = 64, message = "敏感词长度不能超过 64")
    private String word;
    @Schema(description = "状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    @NotNull(message = "状态不能为空")
    @InEnum(value = CommonStatusEnum.class, message = "状态必须是 {value}")
    private Integer status; // å‚见 CommonStatusEnum æžšä¸¾ç±»ï¼ˆ0 å¯ç”¨ / 1 ç¦ç”¨ï¼‰
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/ImStatisticsManagerController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,160 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.statistics;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
import cn.iocoder.yudao.framework.common.util.date.LocalDateTimeUtils;
import cn.iocoder.yudao.module.im.controller.admin.manager.statistics.vo.*;
import cn.iocoder.yudao.module.im.service.statistics.ImStatisticsManagerService;
import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import org.springframework.security.access.prepost.PreAuthorize;
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 java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
@Tag(name = "管理后台 - IM æ•°æ®çœ‹æ¿")
@RestController
@RequestMapping("/im/manager/statistics")
@Validated
public class ImStatisticsManagerController {
    /**
     * ç¾¤è§„模分桶名称的展示顺序
     */
    private static final List<String> GROUP_SIZE_BUCKETS = Arrays.asList("1-9 äºº", "10-49 äºº", "50-199 äºº", "200+ äºº");
    /**
     * çœ‹æ¿åˆ†å¸ƒé»˜è®¤æ—¶é—´çª—口(天)
     */
    private static final int DISTRIBUTION_WINDOW_DAYS = 30;
    /**
     * TOP å‘送者数量
     */
    private static final int TOP_SENDER_LIMIT = 10;
    @Resource
    private ImStatisticsManagerService statisticsService;
    @Resource
    private AdminUserApi adminUserApi;
    @GetMapping("/overview")
    @Operation(summary = "获得数据概览")
    @PreAuthorize("@ss.hasPermission('im:manager:statistics:query')")
    public CommonResult<ImStatisticsManagerOverviewRespVO> getOverview() {
        LocalDateTime todayBegin = LocalDate.now().atStartOfDay();
        LocalDateTime tomorrowBegin = todayBegin.plusDays(1);
        LocalDateTime yesterdayBegin = todayBegin.minusDays(1);
        // å‘¨æ´»/月活定义为 N å¤©æ»šåŠ¨çª—å£ï¼ˆå«ä»Šå¤©ï¼‰
        LocalDateTime weekBegin = todayBegin.minusDays(6);
        LocalDateTime monthBegin = todayBegin.minusDays(29);
        return success(new ImStatisticsManagerOverviewRespVO()
                .setTotalUser(statisticsService.getTotalUserCount())
                .setNewUserToday(statisticsService.getNewUserCount(todayBegin, tomorrowBegin))
                .setTotalGroup(statisticsService.getTotalGroupCount())
                .setNewGroupToday(statisticsService.getNewGroupCount(todayBegin, tomorrowBegin))
                .setActiveUserDaily(statisticsService.getActiveUserCount(todayBegin, tomorrowBegin))
                .setActiveUserWeekly(statisticsService.getActiveUserCount(weekBegin, tomorrowBegin))
                .setActiveUserMonthly(statisticsService.getActiveUserCount(monthBegin, tomorrowBegin))
                .setPrivateMessageToday(statisticsService.getPrivateMessageCount(todayBegin, tomorrowBegin))
                .setGroupMessageToday(statisticsService.getGroupMessageCount(todayBegin, tomorrowBegin))
                .setPrivateMessageYesterday(statisticsService.getPrivateMessageCount(yesterdayBegin, todayBegin))
                .setGroupMessageYesterday(statisticsService.getGroupMessageCount(yesterdayBegin, todayBegin)));
    }
    @GetMapping("/message-trend")
    @Operation(summary = "获得消息趋势(私聊 / ç¾¤èŠåŒçº¿ï¼‰")
    @Parameter(name = "days", description = "回看天数(含今日)", example = "7")
    @PreAuthorize("@ss.hasPermission('im:manager:statistics:query')")
    public CommonResult<ImStatisticsManagerTrendRespVO> getMessageTrend(
            @RequestParam(value = "days", defaultValue = "7") @Min(1) @Max(90) int days) {
        List<LocalDateTime> dates = LocalDateTimeUtils.getLatestDays(days);
        LocalDateTime beginTime = dates.get(0);
        LocalDateTime endTime = dates.get(days - 1).plusDays(1);
        Map<LocalDateTime, Long> privateMap = statisticsService.getPrivateMessageDailyCountMap(beginTime, endTime);
        Map<LocalDateTime, Long> groupMap = statisticsService.getGroupMessageDailyCountMap(beginTime, endTime);
        // è½¬æ¢æ ¼å¼
        Map<String, List<Long>> series = new LinkedHashMap<>();
        series.put("private", alignSeries(dates, privateMap));
        series.put("group", alignSeries(dates, groupMap));
        return success(new ImStatisticsManagerTrendRespVO().setDates(dates).setSeries(series));
    }
    @GetMapping("/user-trend")
    @Operation(summary = "获得用户趋势(新增注册 / æ—¥æ´»åŒçº¿ï¼‰")
    @Parameter(name = "days", description = "回看天数(含今日)", example = "7")
    @PreAuthorize("@ss.hasPermission('im:manager:statistics:query')")
    public CommonResult<ImStatisticsManagerTrendRespVO> getUserTrend(
            @RequestParam(value = "days", defaultValue = "7") @Min(1) @Max(90) int days) {
        List<LocalDateTime> dates = LocalDateTimeUtils.getLatestDays(days);
        LocalDateTime beginTime = dates.get(0);
        LocalDateTime endTime = dates.get(days - 1).plusDays(1);
        Map<LocalDateTime, Long> registerMap = statisticsService.getNewUserDailyCountMap(beginTime, endTime);
        Map<LocalDateTime, Long> activeMap = statisticsService.getActiveUserDailyCountMap(beginTime, endTime);
        // è½¬æ¢æ ¼å¼
        Map<String, List<Long>> series = new LinkedHashMap<>();
        series.put("register", alignSeries(dates, registerMap));
        series.put("active", alignSeries(dates, activeMap));
        return success(new ImStatisticsManagerTrendRespVO().setDates(dates).setSeries(series));
    }
    @GetMapping("/message-type-distribution")
    @Operation(summary = "获得内容类型分布(最近 30 å¤©ï¼‰")
    @PreAuthorize("@ss.hasPermission('im:manager:statistics:query')")
    public CommonResult<List<ImStatisticsManagerMessageTypeRespVO>> getMessageTypeDistribution() {
        LocalDateTime endTime = LocalDate.now().plusDays(1).atStartOfDay();
        LocalDateTime beginTime = endTime.minusDays(DISTRIBUTION_WINDOW_DAYS);
        Map<Integer, Long> typeCountMap = statisticsService.getMessageTypeCountMap(beginTime, endTime);
        // è½¬æ¢æ ¼å¼
        return success(convertList(typeCountMap.entrySet(), entry -> new ImStatisticsManagerMessageTypeRespVO()
                .setType(entry.getKey()).setValue(entry.getValue())));
    }
    @GetMapping("/group-size-distribution")
    @Operation(summary = "获得群规模分布")
    @PreAuthorize("@ss.hasPermission('im:manager:statistics:query')")
    public CommonResult<List<ImStatisticsManagerGroupSizeRespVO>> getGroupSizeDistribution() {
        Map<String, Long> groupSizeMap = statisticsService.getGroupSizeCountMap();
        // è½¬æ¢æ ¼å¼
        return success(convertList(GROUP_SIZE_BUCKETS, bucket -> new ImStatisticsManagerGroupSizeRespVO()
                .setRange(bucket).setCount(groupSizeMap.getOrDefault(bucket, 0L))));
    }
    @GetMapping("/top-senders")
    @Operation(summary = "获得消息 TOP å‘送者(最近 30 å¤©ï¼‰")
    @PreAuthorize("@ss.hasPermission('im:manager:statistics:query')")
    public CommonResult<List<ImStatisticsManagerTopSenderRespVO>> getTopSenders() {
        LocalDateTime endTime = LocalDate.now().plusDays(1).atStartOfDay();
        LocalDateTime beginTime = endTime.minusDays(DISTRIBUTION_WINDOW_DAYS);
        Map<Long, Long> topSenderMap = statisticsService.getTopSenderCountMap(beginTime, endTime, TOP_SENDER_LIMIT);
        // TOP å‘送者:批量回填昵称
        Map<Long, AdminUserRespDTO> userMap = adminUserApi.getUserMap(topSenderMap.keySet());
        return success(convertList(topSenderMap.entrySet(), entry -> {
            ImStatisticsManagerTopSenderRespVO item = new ImStatisticsManagerTopSenderRespVO()
                    .setUserId(entry.getKey()).setMessageCount(entry.getValue());
            MapUtils.findAndThen(userMap, entry.getKey(), user -> item.setNickname(user.getNickname()));
            return item;
        }));
    }
    /**
     * æŠŠæ¯æ—¥èšåˆ Map å¯¹é½åˆ° dates åºåˆ—;缺失天补 0
     */
    private static List<Long> alignSeries(List<LocalDateTime> dates, Map<LocalDateTime, Long> dailyMap) {
        return convertList(dates, date -> dailyMap.getOrDefault(date, 0L));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerGroupSizeRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - IM æ•°æ®çœ‹æ¿ç¾¤è§„模分布项 Response VO")
@Data
public class ImStatisticsManagerGroupSizeRespVO {
    @Schema(description = "区间名称", requiredMode = Schema.RequiredMode.REQUIRED, example = "1-9 äºº")
    private String range;
    @Schema(description = "群数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "320")
    private Long count;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerMessageTypeRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,16 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - IM æ•°æ®çœ‹æ¿å†…容类型分布项 Response VO")
@Data
public class ImStatisticsManagerMessageTypeRespVO {
    @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    private Integer type; // å‚见 ImContentTypeEnum æžšä¸¾ç±»
    @Schema(description = "消息数", requiredMode = Schema.RequiredMode.REQUIRED, example = "8000")
    private Long value;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerOverviewRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,43 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - IM æ•°æ®çœ‹æ¿æ¦‚览 Response VO")
@Data
public class ImStatisticsManagerOverviewRespVO {
    @Schema(description = "用户总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "12345")
    private Long totalUser;
    @Schema(description = "今日新增用户数", requiredMode = Schema.RequiredMode.REQUIRED, example = "23")
    private Long newUserToday;
    @Schema(description = "群总数", requiredMode = Schema.RequiredMode.REQUIRED, example = "678")
    private Long totalGroup;
    @Schema(description = "今日新建群数", requiredMode = Schema.RequiredMode.REQUIRED, example = "4")
    private Long newGroupToday;
    @Schema(description = "日活用户(今日发过消息的去重用户数)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1023")
    private Long activeUserDaily;
    @Schema(description = "周活用户(最近 7 å¤©å‘过消息的去重用户数)", requiredMode = Schema.RequiredMode.REQUIRED, example = "4567")
    private Long activeUserWeekly;
    @Schema(description = "月活用户(最近 30 å¤©å‘过消息的去重用户数)", requiredMode = Schema.RequiredMode.REQUIRED, example = "8901")
    private Long activeUserMonthly;
    @Schema(description = "今日私聊消息数", requiredMode = Schema.RequiredMode.REQUIRED, example = "8765")
    private Long privateMessageToday;
    @Schema(description = "今日群聊消息数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3210")
    private Long groupMessageToday;
    @Schema(description = "昨日私聊消息数", requiredMode = Schema.RequiredMode.REQUIRED, example = "7890")
    private Long privateMessageYesterday;
    @Schema(description = "昨日群聊消息数", requiredMode = Schema.RequiredMode.REQUIRED, example = "3000")
    private Long groupMessageYesterday;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerTopSenderRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,19 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@Schema(description = "管理后台 - IM æ•°æ®çœ‹æ¿ TOP å‘送者项 Response VO")
@Data
public class ImStatisticsManagerTopSenderRespVO {
    @Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long userId;
    @Schema(description = "用户昵称", example = "张三")
    private String nickname;
    @Schema(description = "消息数", requiredMode = Schema.RequiredMode.REQUIRED, example = "1500")
    private Long messageCount;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/manager/statistics/vo/ImStatisticsManagerTrendRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.im.controller.admin.manager.statistics.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
@Schema(description = "管理后台 - IM æ•°æ®çœ‹æ¿è¶‹åŠ¿ Response VO")
@Data
public class ImStatisticsManagerTrendRespVO {
    @Schema(description = "横轴日期序列(每天 00:00:00,由前端按需取日期部分)", requiredMode = Schema.RequiredMode.REQUIRED)
    private List<LocalDateTime> dates;
    @Schema(description = "数据系列:key ä¸ºç³»åˆ—名(如 private/group æˆ– register/active),value ä¸ºä¸Ž dates ç­‰é•¿çš„计数数组",
            requiredMode = Schema.RequiredMode.REQUIRED)
    private Map<String, List<Long>> series;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImChannelMessageController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,75 @@
package cn.iocoder.yudao.module.im.controller.admin.message;
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.message.vo.channel.ImChannelMessagePullRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO;
import cn.iocoder.yudao.module.im.enums.message.ImMessageReceiptStatusEnum;
import cn.iocoder.yudao.module.im.service.message.ImChannelMessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.PositiveOrZero;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
import static cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils.getLoginUserId;
@Tag(name = "用户 APP - IM é¢‘道消息")
@RestController
@RequestMapping("/im/channel/message")
@Validated
public class ImChannelMessageController {
    @Resource
    private ImChannelMessageService channelMessageService;
    @GetMapping("/pull")
    @Operation(summary = "拉取频道消息(离线增量);按 minId æ¸¸æ ‡åˆ†é¡µ")
    public CommonResult<List<ImChannelMessagePullRespVO>> pullChannelMessageList(
            @RequestParam(value = "minId", defaultValue = "0") @PositiveOrZero(message = "minId ä¸èƒ½å°äºŽ 0") Long minId,
            @RequestParam(value = "size", defaultValue = "100")
            @Min(value = 1, message = "size å¿…须大于 0")
            @Max(value = 200, message = "size ä¸€æ¬¡æœ€å¤š 200 æ¡") Integer size) {
        // 1. æ‹‰å–消息列表
        Long userId = getLoginUserId();
        List<ImChannelMessageDO> list = channelMessageService.pullChannelMessageList(userId, minId, size);
        if (CollUtil.isEmpty(list)) {
            return success(Collections.emptyList());
        }
        // 2. æŒ‰å·²è¯»æ¸¸æ ‡è¡¥ receiptStatus(已读 DONE / æœªè¯» PENDING)
        Map<Long, Long> readMaxByChannel = channelMessageService.getChannelReadMaxMessageIdMap(
                userId, convertSet(list, ImChannelMessageDO::getChannelId));
        return success(BeanUtils.toBean(list, ImChannelMessagePullRespVO.class, vo -> {
            Long readMax = readMaxByChannel.get(vo.getChannelId());
            vo.setReceiptStatus(readMax != null && readMax >= vo.getId()
                    ? ImMessageReceiptStatusEnum.DONE.getStatus()
                    : ImMessageReceiptStatusEnum.PENDING.getStatus());
        }));
    }
    @PutMapping("/read")
    @Operation(summary = "标记频道消息已读")
    @Parameter(name = "channelId", description = "频道编号", required = true, example = "1")
    @Parameter(name = "messageId", description = "已读到的消息编号", required = true, example = "100")
    public CommonResult<Boolean> readChannelMessages(@RequestParam("channelId") Long channelId,
                                                     @RequestParam("messageId") Long messageId) {
        channelMessageService.readChannelMessages(getLoginUserId(), channelId, messageId);
        return success(true);
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImGroupMessageController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package cn.iocoder.yudao.module.im.controller.admin.message;
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.message.vo.group.ImGroupMessageListReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageRespVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.group.ImGroupMessageSendReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO;
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 jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - IM ç¾¤èŠæ¶ˆæ¯")
@RestController
@RequestMapping("/im/message/group")
@Validated
public class ImGroupMessageController {
    @Resource
    private ImGroupMessageService groupMessageService;
    @PostMapping("/send")
    @Operation(summary = "发送群聊消息")
    public CommonResult<ImGroupMessageRespVO> sendGroupMessage(@Valid @RequestBody ImGroupMessageSendReqVO reqVO) {
        ImGroupMessageDO message = groupMessageService.sendGroupMessage(getLoginUserId(), reqVO);
        return success(BeanUtils.toBean(message, ImGroupMessageRespVO.class));
    }
    @GetMapping("/pull")
    @Operation(summary = "拉取群聊消息(增量)")
    @Parameter(name = "minId", description = "最小消息 id", required = true, example = "0")
    @Parameter(name = "size", description = "拉取数量", required = true, example = "100")
    public CommonResult<List<ImGroupMessageRespVO>> pullGroupMessageList(
            @RequestParam("minId") Long minId,
            @RequestParam("size") @Min(value = 1, message = "拉取数量最小值为 1") Integer size) {
        List<ImGroupMessageDO> messages = groupMessageService.pullGroupMessageList(getLoginUserId(), minId, size);
        return success(BeanUtils.toBean(messages, ImGroupMessageRespVO.class));
    }
    @PutMapping("/read")
    @Operation(summary = "标记群聊消息已读")
    @Parameter(name = "groupId", description = "群编号", required = true, example = "1")
    @Parameter(name = "messageId", description = "已读到的消息编号", required = true, example = "100")
    public CommonResult<Boolean> readGroupMessages(@RequestParam("groupId") Long groupId,
                                                   @RequestParam("messageId") Long messageId) {
        groupMessageService.readGroupMessages(getLoginUserId(), groupId, messageId);
        return success(true);
    }
    @DeleteMapping("/recall")
    @Operation(summary = "撤回群聊消息")
    @Parameter(name = "id", description = "消息编号", required = true, example = "1")
    public CommonResult<ImGroupMessageRespVO> recallGroupMessage(@RequestParam("id") Long id) {
        ImGroupMessageDO message = groupMessageService.recallGroupMessage(getLoginUserId(), id);
        return success(BeanUtils.toBean(message, ImGroupMessageRespVO.class));
    }
    @GetMapping("/get-read-user-ids")
    @Operation(summary = "获取群消息已读用户列表")
    @Parameter(name = "groupId", description = "群编号", required = true, example = "1")
    @Parameter(name = "messageId", description = "消息编号", required = true, example = "1")
    public CommonResult<List<Long>> getGroupReadUserIds(@RequestParam("groupId") Long groupId,
                                                        @RequestParam("messageId") Long messageId) {
        return success(groupMessageService.getGroupReadUserIds(getLoginUserId(), groupId, messageId));
    }
    @GetMapping("/list")
    @Operation(summary = "查询群聊历史消息")
    public CommonResult<List<ImGroupMessageRespVO>> getGroupMessageList(@Valid ImGroupMessageListReqVO reqVO) {
        List<ImGroupMessageDO> messages = groupMessageService.getGroupMessageList(getLoginUserId(), reqVO);
        return success(BeanUtils.toBean(messages, ImGroupMessageRespVO.class));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImMessageController.http
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
### è¯·æ±‚ /send æŽ¥å£ => æˆåŠŸ
POST {{baseUrl}}/im/message/send
Authorization: Bearer {{token}}
Content-Type: application/json
tenant-id: {{adminTenentId}}
{
      "clientMessageId": "123",
        "receiverId": 1,
        "conversationType": 1,
        "contentType": 101,
        "content": "你好1"
}
### è¯·æ±‚ /pull æŽ¥å£ => æˆåŠŸ
GET {{baseUrl}}/im/message/pull?sequence=0&size=2
Authorization: Bearer {{token}}
tenant-id: {{adminTenentId}}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/ImPrivateMessageController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package cn.iocoder.yudao.module.im.controller.admin.message;
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.message.vo.privates.ImPrivateMessageListReqVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageRespVO;
import cn.iocoder.yudao.module.im.controller.admin.message.vo.privates.ImPrivateMessageSendReqVO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO;
import cn.iocoder.yudao.module.im.service.message.ImPrivateMessageService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - IM ç§èŠæ¶ˆæ¯")
@RestController
@RequestMapping("/im/message/private")
@Validated
public class ImPrivateMessageController {
    @Resource
    private ImPrivateMessageService privateMessageService;
    @PostMapping("/send")
    @Operation(summary = "发送私聊消息")
    public CommonResult<ImPrivateMessageRespVO> sendPrivateMessage(
            @Valid @RequestBody ImPrivateMessageSendReqVO reqVO) {
        ImPrivateMessageDO message = privateMessageService.sendPrivateMessage(getLoginUserId(), reqVO);
        return success(BeanUtils.toBean(message, ImPrivateMessageRespVO.class));
    }
    @GetMapping("/pull")
    @Operation(summary = "拉取私聊消息(增量)")
    @Parameter(name = "minId", description = "最小消息 id", required = true, example = "0")
    @Parameter(name = "size", description = "拉取数量", required = true, example = "100")
    public CommonResult<List<ImPrivateMessageRespVO>> pullPrivateMessageList(
            @RequestParam("minId") Long minId,
            @RequestParam("size") @Min(value = 1, message = "拉取数量最小值为 1") Integer size) {
        List<ImPrivateMessageDO> messages = privateMessageService.pullPrivateMessageList(getLoginUserId(), minId, size);
        return success(BeanUtils.toBean(messages, ImPrivateMessageRespVO.class));
    }
    @PutMapping("/read")
    @Operation(summary = "标记私聊消息已读")
    @Parameter(name = "receiverId", description = "接收方用户编号(对方)", required = true, example = "2")
    @Parameter(name = "messageId", description = "已读位置(含),通常是会话内最大消息编号", required = true, example = "100")
    public CommonResult<Boolean> readPrivateMessages(@RequestParam("receiverId") Long receiverId,
                                                     @RequestParam("messageId") Long messageId) {
        privateMessageService.readPrivateMessages(getLoginUserId(), receiverId, messageId);
        return success(true);
    }
    @GetMapping("/max-read-message-id")
    @Operation(summary = "查询对方已读到我发的最大消息 id",
            description = "用于多端 / ç¦»çº¿åœºæ™¯ä¸‹çš„已读位置补齐:进入会话或断线重连后调用,据此把本地自发消息更新为已读")
    @Parameter(name = "peerId", description = "对方用户编号", required = true, example = "2")
    public CommonResult<Long> getMaxReadMessageId(@RequestParam("peerId") Long peerId) {
        return success(privateMessageService.getMaxReadMessageId(getLoginUserId(), peerId));
    }
    @DeleteMapping("/recall")
    @Operation(summary = "撤回私聊消息")
    @Parameter(name = "id", description = "消息编号", required = true, example = "1")
    public CommonResult<ImPrivateMessageRespVO> recallPrivateMessage(@RequestParam("id") Long id) {
        ImPrivateMessageDO message = privateMessageService.recallPrivateMessage(getLoginUserId(), id);
        return success(BeanUtils.toBean(message, ImPrivateMessageRespVO.class));
    }
    @GetMapping("/list")
    @Operation(summary = "查询私聊历史消息")
    public CommonResult<List<ImPrivateMessageRespVO>> getPrivateMessageList(@Valid ImPrivateMessageListReqVO reqVO) {
        List<ImPrivateMessageDO> messages = privateMessageService.getPrivateMessageList(getLoginUserId(), reqVO);
        return success(BeanUtils.toBean(messages, ImPrivateMessageRespVO.class));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/channel/ImChannelMessagePullRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo.channel;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
@Schema(description = "用户 APP - IM é¢‘道消息拉取 Response VO")
@Data
public class ImChannelMessagePullRespVO {
    @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9001")
    private Long id;
    @Schema(description = "频道编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    private Long channelId;
    @Schema(description = "素材编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1024")
    private Long materialId;
    @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "125")
    private Integer type;
    @Schema(description = "消息内容;payload JSON å¿«ç…§", requiredMode = Schema.RequiredMode.REQUIRED)
    private String content;
    @Schema(description = "当前用户回执 / å·²è¯»æ€ï¼›æŒ‰ Redis è¯»ä½ç½®è®¡ç®—(已读 DONE,未读 PENDING)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
    private Integer receiptStatus; // å‚见 ImMessageReceiptStatusEnum æžšä¸¾ç±»
    @Schema(description = "发送时间", requiredMode = Schema.RequiredMode.REQUIRED)
    private LocalDateTime sendTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/group/ImGroupMessageListReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,26 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo.group;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Schema(description = "管理后台 - ç¾¤èŠåŽ†å²æ¶ˆæ¯åˆ—è¡¨ Request VO")
@Data
public class ImGroupMessageListReqVO {
    @Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "10")
    @NotNull(message = "群编号不能为空")
    private Long groupId;
    @Schema(description = "起始消息编号,从该 id å¾€å‰æ‹‰å–(不含)。为空则从最新消息开始", example = "1024")
    private Long maxId;
    @Schema(description = "拉取数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
    @NotNull(message = "拉取数量不能为空")
    @Min(value = 1, message = "拉取数量最小值为 1")
    @Max(value = 200, message = "拉取数量最大值为 200")
    private Integer limit;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/group/ImGroupMessageRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,52 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo.group;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.util.List;
/**
 * ç¾¤èŠæ¶ˆæ¯ Response VO
 */
@Schema(description = "管理后台 - ç¾¤èŠæ¶ˆæ¯ Response VO")
@Data
public class ImGroupMessageRespVO {
    @Schema(description = "消息编号", example = "1")
    private Long id;
    @Schema(description = "客户端消息编号", example = "uuid-xxx")
    private String clientMessageId;
    @Schema(description = "发送人编号", example = "1")
    private Long senderId;
    @Schema(description = "群编号", example = "1")
    private Long groupId;
    @Schema(description = "消息类型", example = "0")
    private Integer type;
    @Schema(description = "消息内容", example = "{\"content\":\"你好\"}")
    private String content;
    @Schema(description = "消息状态", example = "0")
    private Integer status;
    @Schema(description = "发送时间")
    private LocalDateTime sendTime;
    @Schema(description = "@目标用户编号列表", example = "[1,2,3]")
    private List<Long> atUserIds;
    @Schema(description = "定向接收用户编号列表", example = "[1,2]")
    private List<Long> receiverUserIds;
    @Schema(description = "回执状态", example = "0")
    private Integer receiptStatus;
    @Schema(description = "已读人数(回执消息、且发送人为当前用户时有值)", example = "3")
    private Integer readCount;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/group/ImGroupMessageSendReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,53 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo.group;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.ImContentTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.List;
/**
 * ç¾¤èŠæ¶ˆæ¯å‘送 Request VO
 */
@Schema(description = "管理后台 - ç¾¤èŠæ¶ˆæ¯å‘送 Request VO")
@Data
public class ImGroupMessageSendReqVO {
    @Schema(description = "客户端消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "uuid-xxx")
    @NotEmpty(message = "客户端消息编号不能为空")
    private String clientMessageId;
    @Schema(description = "群编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "群编号不能为空")
    private Long groupId;
    @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    @NotNull(message = "消息类型不能为空")
    @InEnum(ImContentTypeEnum.class)
    private Integer type;
    @Schema(description = "消息内容,JSON æ ¼å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "{\"content\":\"你好\"}")
    @NotEmpty(message = "消息内容不能为空")
    private String content;
    @Schema(description = "@目标用户编号列表", example = "[1,2,3]")
    private List<Long> atUserIds;
    @Schema(description = "是否需要回执", example = "false")
    private Boolean receipt;
    /**
     * ä»…允许用户消息(normal)类型
     */
    @AssertTrue(message = "消息类型不允许")
    @JsonIgnore
    public boolean isTypeNormal() {
        return type == null || ImContentTypeEnum.validate(type).isNormal();
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessageListReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,27 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo.privates;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - ç§èŠåŽ†å²æ¶ˆæ¯åˆ—è¡¨ Request VO")
@Data
public class ImPrivateMessageListReqVO {
    @Schema(description = "接收人编号(对方)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
    @NotNull(message = "接收人编号不能为空")
    private Long receiverId;
    @Schema(description = "起始消息编号,从该 id å¾€å‰æ‹‰å–(不含)。为空则从最新消息开始", example = "1024")
    private Long maxId;
    @Schema(description = "拉取数量", requiredMode = Schema.RequiredMode.REQUIRED, example = "20")
    @NotNull(message = "拉取数量不能为空")
    @Min(value = 1, message = "拉取数量最小值为 1")
    @Max(value = 200, message = "拉取数量最大值为 200")
    private Integer limit;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessagePageReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo.privates;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import jakarta.validation.constraints.NotNull;
@Schema(description = "管理后台 - ç§èŠæ¶ˆæ¯åˆ†é¡µ Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class ImPrivateMessagePageReqVO extends PageParam {
    @Schema(description = "接收人编号(对方)", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
    @NotNull(message = "接收人编号不能为空")
    private Long receiverId;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessageRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,42 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo.privates;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
/**
 * ç§èŠæ¶ˆæ¯ Response VO
 */
@Schema(description = "管理后台 - ç§èŠæ¶ˆæ¯ Response VO")
@Data
public class ImPrivateMessageRespVO {
    @Schema(description = "消息编号", example = "1")
    private Long id;
    @Schema(description = "客户端消息编号", example = "uuid-xxx")
    private String clientMessageId;
    @Schema(description = "发送人编号", example = "1")
    private Long senderId;
    @Schema(description = "接收人编号", example = "2")
    private Long receiverId;
    @Schema(description = "消息类型", example = "0")
    private Integer type;
    @Schema(description = "消息内容", example = "{\"content\":\"你好\"}")
    private String content;
    @Schema(description = "消息状态", example = "0")
    private Integer status; // å‚见 ImMessageStatusEnum æžšä¸¾ç±»
    @Schema(description = "回执状态", example = "0")
    private Integer receiptStatus; // å‚见 ImMessageReceiptStatusEnum æžšä¸¾ç±»
    @Schema(description = "发送时间")
    private LocalDateTime sendTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/message/vo/privates/ImPrivateMessageSendReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,48 @@
package cn.iocoder.yudao.module.im.controller.admin.message.vo.privates;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.ImContentTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
/**
 * ç§èŠæ¶ˆæ¯å‘送 Request VO
 */
@Schema(description = "管理后台 - ç§èŠæ¶ˆæ¯å‘送 Request VO")
@Data
public class ImPrivateMessageSendReqVO {
    @Schema(description = "客户端消息编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "uuid-xxx")
    @NotEmpty(message = "客户端消息编号不能为空")
    private String clientMessageId;
    @Schema(description = "接收人编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
    @NotNull(message = "接收人编号不能为空")
    private Long receiverId;
    @Schema(description = "消息类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "0")
    @NotNull(message = "消息类型不能为空")
    @InEnum(ImContentTypeEnum.class)
    private Integer type;
    @Schema(description = "消息内容,JSON æ ¼å¼", requiredMode = Schema.RequiredMode.REQUIRED, example = "{\"content\":\"你好\"}")
    @NotEmpty(message = "消息内容不能为空")
    private String content;
    @Schema(description = "是否需要回执", example = "false")
    private Boolean receipt;
    /**
     * ä»…允许用户消息(normal)类型
     */
    @AssertTrue(message = "消息类型不允许")
    @JsonIgnore
    public boolean isTypeNormal() {
        return type == null || ImContentTypeEnum.validate(type).isNormal();
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/ImRtcCallController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,169 @@
package cn.iocoder.yudao.module.im.controller.admin.rtc;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
import cn.iocoder.yudao.module.im.controller.admin.rtc.vo.ImRtcCallCreateReqVO;
import cn.iocoder.yudao.module.im.controller.admin.rtc.vo.ImRtcCallInviteReqVO;
import cn.iocoder.yudao.module.im.controller.admin.rtc.vo.ImRtcCallRespVO;
import cn.iocoder.yudao.module.im.controller.admin.rtc.vo.ImRtcGroupCallRespVO;
import cn.iocoder.yudao.module.im.dal.dataobject.rtc.ImRtcCallDO;
import cn.iocoder.yudao.module.im.dal.dataobject.rtc.ImRtcParticipantDO;
import cn.iocoder.yudao.module.im.enums.rtc.ImRtcCallStatusEnum;
import cn.iocoder.yudao.module.im.enums.rtc.ImRtcParticipantStatusEnum;
import cn.iocoder.yudao.module.im.framework.config.ImProperties;
import cn.iocoder.yudao.module.im.service.rtc.ImRtcCallService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.validation.Valid;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Tag(name = "管理后台 - IM å®žæ—¶é€šè¯")
@RestController
@RequestMapping("/im/rtc")
@Validated
public class ImRtcCallController {
    @Resource
    private ImRtcCallService rtcCallService;
    @Resource
    private ImProperties imProperties;
    @PostMapping("/create")
    @Operation(summary = "创建新通话;按 conversationType åŒºåˆ†ç§èŠ / ç¾¤èŠ")
    public CommonResult<ImRtcCallRespVO> createCall(@Valid @RequestBody ImRtcCallCreateReqVO reqVO) {
        Long userId = getLoginUserId();
        ImRtcCallDO call = rtcCallService.createCall(userId, reqVO);
        return success(buildCallRespVO(call, userId));
    }
    @PostMapping("/invite")
    @Operation(summary = "通话中追加邀请;仅群通话可用")
    public CommonResult<Boolean> inviteCall(@Valid @RequestBody ImRtcCallInviteReqVO reqVO) {
        rtcCallService.inviteCall(getLoginUserId(), reqVO);
        return success(true);
    }
    @PostMapping("/join")
    @Operation(summary = "加入已有群通话;用于胶囊条「加入」按钮")
    @Parameter(name = "room", description = "业务通话编号", required = true, example = "f47ac10b58cc4372a567")
    public CommonResult<ImRtcCallRespVO> joinCall(@RequestParam("room") String room) {
        Long userId = getLoginUserId();
        return success(buildCallRespVO(rtcCallService.joinCall(userId, room), userId));
    }
    @PostMapping("/accept")
    @Operation(summary = "接听通话")
    @Parameter(name = "room", description = "业务通话编号", required = true, example = "f47ac10b58cc4372a567")
    public CommonResult<ImRtcCallRespVO> accept(@RequestParam("room") String room) {
        Long userId = getLoginUserId();
        return success(buildCallRespVO(rtcCallService.acceptCall(userId, room), userId));
    }
    @PostMapping("/reject")
    @Operation(summary = "拒绝通话")
    @Parameter(name = "room", description = "业务通话编号", required = true, example = "f47ac10b58cc4372a567")
    public CommonResult<Boolean> reject(@RequestParam("room") String room) {
        rtcCallService.rejectCall(getLoginUserId(), room);
        return success(true);
    }
    @PostMapping("/cancel")
    @Operation(summary = "取消邀请;主叫接通前调用")
    @Parameter(name = "room", description = "业务通话编号", required = true, example = "f47ac10b58cc4372a567")
    public CommonResult<Boolean> cancel(@RequestParam("room") String room) {
        rtcCallService.cancelCall(getLoginUserId(), room);
        return success(true);
    }
    @PostMapping("/leave")
    @Operation(summary = "离开通话;接通后调用")
    @Parameter(name = "room", description = "业务通话编号", required = true, example = "f47ac10b58cc4372a567")
    public CommonResult<Boolean> leave(@RequestParam("room") String room) {
        rtcCallService.leaveCall(getLoginUserId(), room);
        return success(true);
    }
    @PostMapping("/no-answer-call-check")
    @Operation(summary = "前端 RUNNING ç«¯ timer å…œåº•;触发后端立即扫描该 room çš„æŒ¯é“ƒè¶…时(接口静默)")
    @Parameter(name = "room", description = "业务通话编号", required = true, example = "f47ac10b58cc4372a567")
    public CommonResult<Boolean> noAnswerCallCheck(@RequestParam("room") String room) {
        rtcCallService.noAnswerCallCheck(getLoginUserId(), room);
        return success(true);
    }
    @GetMapping("/get-active-call")
    @Operation(summary = "查询当前进行中的通话;用于群聊顶部「N äººæ­£åœ¨é€šè¯ã€èƒ¶å›Šæ¡")
    @Parameter(name = "groupId", description = "群编号", required = true, example = "2048")
    public CommonResult<ImRtcGroupCallRespVO> getActiveCall(@RequestParam("groupId") Long groupId) {
        ImRtcCallDO call = rtcCallService.getActiveCall(getLoginUserId(), groupId);
        return success(buildGroupActiveRespVO(call));
    }
    // ========== VO æ‹¼è£… ==========
    /**
     * æ‹¼è£… invite / join / accept / refresh-token çš„响应 VO;含 token + å‚与者分桶
     *
     * @param call   é€šè¯ä¸»è¡¨
     * @param userId å½“前用户编号;token æŒ‰è¯¥ç”¨æˆ·ç­¾å‘
     * @return å“åº” VO;call ä¸ºç©ºè¿”回 null
     */
    private ImRtcCallRespVO buildCallRespVO(ImRtcCallDO call, Long userId) {
        if (call == null) {
            return null;
        }
        List<ImRtcParticipantDO> participants = rtcCallService.getCallParticipantList(call.getRoom());
        boolean ended = ImRtcCallStatusEnum.isEnded(call.getStatus());
        return new ImRtcCallRespVO()
                .setRoom(call.getRoom())
                .setLivekitUrl(imProperties.getRtc().getLivekitUrl())
                // ä»…非 ENDED åœºæ™¯æ‰ç­¾ token,ENDED åœºæ™¯ä¸ç­¾ï¼Œå‰ç«¯æ ¹æ® token æ˜¯å¦å­˜åœ¨ï¼Œæ¥åˆ¤æ–­æ˜¯å¦å±•示「通话已结束」的提示
                .setToken(ended ? null : rtcCallService.signCallToken(userId, call.getRoom()))
                .setConversationType(call.getConversationType()).setMediaType(call.getMediaType())
                .setStatus(call.getStatus()).setEndReason(call.getEndReason())
                .setInviterId(call.getInviterUserId()).setGroupId(call.getGroupId())
                .setInviteeIds(filterUserIds(participants, ImRtcParticipantStatusEnum.INVITING))
                .setJoinedUserIds(filterUserIds(participants, ImRtcParticipantStatusEnum.JOINED));
    }
    /**
     * æ‹¼è£… get-active-call çš„响应 VO
     *
     * @param call é€šè¯ä¸»è¡¨
     * @return å“åº” VO:只用于群聊胶囊条,不含 token
     */
    private ImRtcGroupCallRespVO buildGroupActiveRespVO(ImRtcCallDO call) {
        if (call == null) {
            return null;
        }
        List<ImRtcParticipantDO> participants = rtcCallService.getCallParticipantList(call.getRoom());
        return new ImRtcGroupCallRespVO().setRoom(call.getRoom()).setMediaType(call.getMediaType())
                .setGroupId(call.getGroupId()).setInviterId(call.getInviterUserId())
                .setJoinedUserIds(filterUserIds(participants, ImRtcParticipantStatusEnum.JOINED))
                .setInviteeIds(filterUserIds(participants, ImRtcParticipantStatusEnum.INVITING));
    }
    /**
     * æŒ‰çŠ¶æ€è¿‡æ»¤å‚ä¸Žè€… userId;用 LinkedHashSet ä¿ç•™å‰ç«¯å±•示顺序
     *
     * @param participants å‚与者列表
     * @param status       ç›®æ ‡çŠ¶æ€
     * @return userId é›†åˆ
     */
    private static Set<Long> filterUserIds(List<ImRtcParticipantDO> participants,
                                           ImRtcParticipantStatusEnum status) {
        return CollectionUtils.convertLinkedSet(participants, ImRtcParticipantDO::getUserId,
                participant -> Objects.equals(participant.getStatus(), status.getStatus()));
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/ImRtcLiveKitController.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,76 @@
package cn.iocoder.yudao.module.im.controller.admin.rtc;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.util.json.JsonUtils;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.module.im.framework.rtc.core.LiveKitClient;
import cn.iocoder.yudao.module.im.framework.rtc.core.LiveKitWebhookEventDTO;
import cn.iocoder.yudao.module.im.service.rtc.ImRtcCallService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
/**
 * LiveKit åŽ‚å•†å¯¹æŽ¥å…¥å£ï¼ˆWebhook å›žè°ƒï¼‰
 * <p>
 * å®‰å…¨ç”±è¯·æ±‚签名(JWT + body sha256)保证,不走登录鉴权;伪造请求会被签名校验直接拒绝
 *
 * @author èŠ‹é“æºç 
 */
@Tag(name = "LiveKit Webhook å›žè°ƒ")
@RestController
@RequestMapping("/im/livekit")
@Slf4j
public class ImRtcLiveKitController {
    @Resource
    private LiveKitClient liveKitClient;
    @Resource
    private ImRtcCallService rtcCallService;
    /**
     * LiveKit Webhook å›žè°ƒå…¥å£
     *
     * @param request HTTP è¯·æ±‚;用于取 Authorization å¤´åšç­¾åæ ¡éªŒ
     * @param rawBody åŽŸå§‹ JSON body;签名校验需要原始字节
     * @return æ˜¯å¦å¤„理成功;伪造 / éžæ³•事件返回 false
     */
    @PostMapping("/webhook")
    @Operation(summary = "LiveKit Webhook å›žè°ƒï¼›æŽ¥æ”¶æˆå‘˜ç¦»å¼€ / æˆ¿é—´ç»“束等事件做业务态兜底清理")
    @PermitAll
    @TenantIgnore
    public CommonResult<Boolean> webhook(HttpServletRequest request, @RequestBody String rawBody) {
        // 1.1 æ ¡éªŒç­¾åï¼›ä¼ªé€ è¯·æ±‚直接 200 ä½†å¿½ç•¥ï¼Œé¿å…ç»™æ”»å‡»è€…反馈
        if (!liveKitClient.verifyWebhookSignature(request.getHeader("Authorization"), rawBody)) {
            log.warn("[webhook][签名校验失败 ip={} bodyLength={}]",
                    request.getRemoteAddr(), rawBody == null ? 0 : rawBody.length());
            return success(false);
        }
        // 1.2 è§£æžäº‹ä»¶è½½è·ï¼›éžæ³• / ç©º event ç›´æŽ¥å¿½ç•¥
        LiveKitWebhookEventDTO event = JsonUtils.parseObject(rawBody, LiveKitWebhookEventDTO.class);
        if (event == null || StrUtil.isBlank(event.getEvent())) {
            return success(false);
        }
        // 2. äº¤ç»™ service å¤„理;幂等由 service è‡ªå·±ä¿è¯
        log.info("[webhook][事件处理 event={} room={}]", event.getEvent(),
                event.getRoom() == null ? null : event.getRoom().getName());
        try {
            rtcCallService.handleLiveKitEvent(event);
        } catch (Exception e) {
            log.error("[webhook][事件处理失败 event={} body={}]", event.getEvent(), rawBody, e);
        }
        return success(true);
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/vo/ImRtcCallCreateReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.im.controller.admin.rtc.vo;
import cn.iocoder.yudao.framework.common.validation.InEnum;
import cn.iocoder.yudao.module.im.enums.ImConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.rtc.ImRtcCallMediaTypeEnum;
import com.fasterxml.jackson.annotation.JsonIgnore;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.AssertTrue;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.Set;
@Schema(description = "管理后台 - åˆ›å»ºé€šè¯ Request VO")
@Data
public class ImRtcCallCreateReqVO {
    @Schema(description = "会话类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
    @NotNull(message = "会话类型不能为空")
    @InEnum(ImConversationTypeEnum.class)
    private Integer conversationType;
    @Schema(description = "媒体类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
    @NotNull(message = "媒体类型不能为空")
    @InEnum(ImRtcCallMediaTypeEnum.class)
    private Integer mediaType;
    @Schema(description = "群编号;群聊场景必填", example = "2048")
    private Long groupId;
    @Schema(description = "被邀请的用户编号集合;私聊必传 1 ä¸ªå¯¹ç«¯ï¼Œç¾¤èŠå¿…传至少 1 äºº")
    private Set<@NotNull(message = "被邀请用户编号不能为空") Long> inviteeIds;
    /**
     * é€šè¯ä»…支持私聊和群聊
     */
    @AssertTrue(message = "会话类型不支持")
    @JsonIgnore
    public boolean isConversationTypeSupported() {
        return conversationType == null || ImConversationTypeEnum.isPrivate(conversationType)
                || ImConversationTypeEnum.isGroup(conversationType);
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/vo/ImRtcCallInviteReqVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.im.controller.admin.rtc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotEmpty;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
import java.util.Set;
@Schema(description = "管理后台 - é€šè¯ä¸­è¿½åŠ é‚€è¯· Request VO;仅群通话可用")
@Data
public class ImRtcCallInviteReqVO {
    @Schema(description = "业务通话编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "f47ac10b58cc4372a567")
    @NotBlank(message = "通话编号不能为空")
    private String room;
    @Schema(description = "新邀请的用户编号集合", requiredMode = Schema.RequiredMode.REQUIRED)
    @NotEmpty(message = "请至少选择一位成员")
    private Set<@NotNull(message = "被邀请用户编号不能为空") Long> inviteeIds;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/vo/ImRtcCallRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,45 @@
package cn.iocoder.yudao.module.im.controller.admin.rtc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Set;
@Schema(description = "管理后台 - é€šè¯ä¼šè¯ Response VO;invite / accept / refreshToken å…±ç”¨")
@Data
public class ImRtcCallRespVO {
    @Schema(description = "业务通话编号")
    private String room;
    @Schema(description = "LiveKit Server WebSocket åœ°å€ï¼›å‰ç«¯ connect ç”¨")
    private String livekitUrl;
    @Schema(description = "LiveKit æŽ¥å…¥ Token;需要时调 refreshToken é‡æ–°ç­¾å‘")
    private String token;
    @Schema(description = "会话类型")
    private Integer conversationType; // å‚见 ImConversationTypeEnum æžšä¸¾ç±»
    @Schema(description = "媒体类型")
    private Integer mediaType; // å‚见 ImCallMediaTypeEnum æžšä¸¾ç±»
    @Schema(description = "状态")
    private Integer status; // å‚见 ImCallStatusEnum æžšä¸¾ç±»
    @Schema(description = "结束原因;仅 status=ENDED æ—¶æœ‰å€¼")
    private Integer endReason; // å‚见 ImRtcCallEndReasonEnum æžšä¸¾ç±»
    @Schema(description = "发起人编号")
    private Long inviterId;
    @Schema(description = "群编号;群通话才有")
    private Long groupId;
    @Schema(description = "被邀请人编号集合")
    private Set<Long> inviteeIds;
    @Schema(description = "已加入房间的成员编号集合")
    private Set<Long> joinedUserIds;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/admin/rtc/vo/ImRtcGroupCallRespVO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,30 @@
package cn.iocoder.yudao.module.im.controller.admin.rtc.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.util.Set;
@Schema(description = "管理后台 - ç¾¤æ´»è·ƒé€šè¯ Response VO;不含 token,胶囊条仅展示用")
@Data
public class ImRtcGroupCallRespVO {
    @Schema(description = "业务通话编号")
    private String room;
    @Schema(description = "群编号")
    private Long groupId;
    @Schema(description = "媒体类型")
    private Integer mediaType; // å‚见 ImCallMediaTypeEnum æžšä¸¾ç±»
    @Schema(description = "发起人编号")
    private Long inviterId;
    @Schema(description = "已加入房间的用户编号集合")
    private Set<Long> joinedUserIds;
    @Schema(description = "被邀请池;用于胶囊条展开时显示待加入头像")
    private Set<Long> inviteeIds;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/app/package-info.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,4 @@
/**
 * å ä½
 */
package cn.iocoder.yudao.module.im.controller.app;
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/controller/package-info.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,6 @@
/**
 * æä¾› RESTful API ç»™å‰ç«¯ï¼š
 * 1. admin åŒ…:提供给管理后台 yudao-ui-admin å‰ç«¯é¡¹ç›®
 * 2. app åŒ…:提供给用户 APP yudao-ui-app å‰ç«¯é¡¹ç›®ï¼Œå®ƒçš„ Controller å’Œ VO éƒ½è¦æ·»åŠ  App å‰ç¼€ï¼Œç”¨äºŽå’Œç®¡ç†åŽå°è¿›è¡ŒåŒºåˆ†
 */
package cn.iocoder.yudao.module.im.controller;
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,57 @@
package cn.iocoder.yudao.module.im.dal.dataobject.channel;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * IM é¢‘道 DO
 * <p>
 * ä¸šåŠ¡è¯­ä¹‰ï¼š
 * - é¢‘道是运营单向推送的主体;C ç«¯ç”¨æˆ·ä¸èƒ½å‘频道发消息
 * - {@link #code} æ˜¯ä¸šåŠ¡ç ï¼ˆAPI / å­—典外露),id æ˜¯æ•°å­—主键给前端会话 targetId ç”¨
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_channel")
@KeySequence("im_channel_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImChannelDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * é¢‘道业务码
     */
    private String code;
    /**
     * é¢‘道名称
     */
    private String name;
    /**
     * é¢‘道头像
     */
    private String avatar;
    /**
     * æŽ’序
     */
    private Integer sort;
    /**
     * çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link CommonStatusEnum}
     */
    private Integer status;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/channel/ImChannelMaterialDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,69 @@
package cn.iocoder.yudao.module.im.dal.dataobject.channel;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO;
import cn.iocoder.yudao.module.im.enums.channel.ImChannelMaterialTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * IM é¢‘道素材 DO
 * <p>
 * ä¸šåŠ¡è¯­ä¹‰ï¼š
 * - è¿è¥ç´ æåº“,可被反复推送
 * - ä¸€æ¡ç´ æ 1:N å…³è”多条 {@link ImChannelMessageDO}
 * - {@link #content} å¯Œæ–‡æœ¬ä»…在素材详情接口按需返回,推送 payload ä¸å¸¦ï¼Œé¿å…åŽ‹çˆ† WebSocket é€šé“
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_channel_material")
@KeySequence("im_channel_material_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImChannelMaterialDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * é¢‘道编号
     * <p>
     * å…³è” {@link ImChannelDO#getId()}
     */
    private Long channelId;
    /**
     * ç´ æå†…容类型
     * <p>
     * æžšä¸¾ {@link ImChannelMaterialTypeEnum}
     */
    private Integer type;
    /**
     * æ ‡é¢˜
     */
    private String title;
    /**
     * å°é¢å›¾
     */
    private String coverUrl;
    /**
     * æ‘˜è¦
     */
    private String summary;
    /**
     * å¯Œæ–‡æœ¬ HTML;在 {@link ImChannelMaterialTypeEnum#CONTENT} ä½¿ç”¨
     */
    private String content;
    /**
     * è·³è½¬é“¾æŽ¥ï¼›åœ¨ {@link ImChannelMaterialTypeEnum#LINK} ä½¿ç”¨
     */
    private String url;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/conversation/ImConversationReadDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,68 @@
package cn.iocoder.yudao.module.im.dal.dataobject.conversation;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.dal.dataobject.channel.ImChannelDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImChannelMessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImGroupMessageDO;
import cn.iocoder.yudao.module.im.dal.dataobject.message.ImPrivateMessageDO;
import cn.iocoder.yudao.module.im.enums.ImConversationTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * IM ä¼šè¯è¯»ä½ç½® DO
 * <p>
 * åªè¡¨è¾¾ã€Œç”¨æˆ·åœ¨æŸä¸ªä¼šè¯çš„æœ€å¤§å·²è¯»ä½ç½®ã€ï¼Œç§èŠ / ç¾¤èŠ / é¢‘道统一落这张表,是读位置的唯一权威。
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_conversation_read")
@KeySequence("im_conversation_read_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImConversationReadDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç”¨æˆ·ç¼–号
     *
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long userId;
    /**
     * ä¼šè¯ç±»åž‹
     * <p>
     * æžšä¸¾ {@link ImConversationTypeEnum}
     */
    private Integer conversationType;
    /**
     * ç›®æ ‡ç¼–号
     * <p>
     * ç§èŠå…³è” AdminUserDO çš„ id;群聊关联 {@link ImGroupDO#getId()};频道关联 {@link ImChannelDO#getId()}
     */
    private Long targetId;
    /**
     * æœ€å¤§å·²è¯»æ¶ˆæ¯ç¼–号
     * <p>
     * å…³è” {@link ImPrivateMessageDO#getId()}、{@link ImGroupMessageDO#getId()}、{@link ImChannelMessageDO#getId()}
     */
    private Long messageId;
    /**
     * æœ€è¿‘已读时间
     */
    private LocalDateTime readTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/face/ImFacePackDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.im.dal.dataobject.face;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * IM è¡¨æƒ…包 DO(运营配置的系统表情包元数据)
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_face_pack")
@KeySequence("im_face_pack_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImFacePackDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * è¡¨æƒ…包名称
     */
    private String name;
    /**
     * è¡¨æƒ…包图标
     * <p>
     * é¢æ¿åº•部 tab æ æ˜¾ç¤º
     */
    private String icon;
    /**
     * æŽ’序
     */
    private Integer sort;
    /**
     * çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link CommonStatusEnum}
     */
    private Integer status;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/face/ImFacePackItemDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,61 @@
package cn.iocoder.yudao.module.im.dal.dataobject.face;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * IM è¡¨æƒ…包项 DO(系统表情包内的单张表情图)
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_face_pack_item")
@KeySequence("im_face_pack_item_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImFacePackItemDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * æ‰€å±žè¡¨æƒ…包编号
     */
    private Long packId;
    /**
     * è¡¨æƒ…图 URL
     */
    private String url;
    /**
     * è¡¨æƒ…名(可选;如「狗头」「捂脸」)
     */
    private String name;
    /**
     * æ¸²æŸ“宽度(像素)
     */
    private Integer width;
    /**
     * æ¸²æŸ“高度(像素)
     */
    private Integer height;
    /**
     * æŽ’序
     */
    private Integer sort;
    /**
     * çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link CommonStatusEnum}
     */
    private Integer status;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/face/ImFaceUserItemDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,58 @@
package cn.iocoder.yudao.module.im.dal.dataobject.face;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/**
 * IM ç”¨æˆ·ç§æœ‰è¡¨æƒ… DO(个人表情包,对照微信「我的表情」)
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_face_user_item")
@KeySequence("im_face_user_item_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImFaceUserItemDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * æ‰€å±žç”¨æˆ·ç¼–号
     * <p>
     * å…³è” AdminUserDO çš„ id ç¼–号
     */
    private Long userId;
    /**
     * è¡¨æƒ…图 URL
     */
    private String url;
    /**
     * è¡¨æƒ…名(可选)
     * <p>
     * ç”¨æˆ·ç§æœ‰è¡¨æƒ…通常不带名字,留字段以备将来「重命名」交互
     */
    private String name;
    /**
     * æ¸²æŸ“宽度(像素)
     */
    private Integer width;
    /**
     * æ¸²æŸ“高度(像素)
     */
    private Integer height;
    /**
     * æŽ’序
     */
    private Integer sort;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/friend/ImFriendDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,90 @@
package cn.iocoder.yudao.module.im.dal.dataobject.friend;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * IM å¥½å‹å…³ç³» DO
 * <p>
 * ä¸šåŠ¡è¯­ä¹‰ï¼š
 * - åŒå‘关系:A-B äº’为好友会存 2 æ¡è®°å½•(userId=A, friendUserId=B å’Œ userId=B, friendUserId=A)
 * - çŠ¶æ€ç®¡ç†ï¼š{@link #status} ä½¿ç”¨ {@link CommonStatusEnum},ENABLE=正常,DISABLE=已删除
 * - å…æ‰“扰:{@link #silent} æŽ§åˆ¶æ˜¯å¦å±è”½æ¥è‡ªè¯¥å¥½å‹çš„通知
 * - è”系人置顶:{@link #pinned} å•边,影响联系人 / ä¼šè¯æŽ’序
 * - é»‘名单:{@link #blocked} å¼±å…³è” friend,单边屏蔽对方消息(必须先是好友)
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_friend")
@KeySequence("im_friend_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImFriendDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç”¨æˆ·ç¼–号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long userId;
    /**
     * å¥½å‹ç”¨æˆ·ç¼–号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long friendUserId;
    /**
     * æ˜¯å¦å…æ‰“扰
     */
    private Boolean silent;
    /**
     * å¥½å‹å±•示备注
     */
    private String displayName;
    /**
     * æ·»åŠ æ¥æº
     * <p>
     * æžšä¸¾ {@link cn.iocoder.yudao.module.im.enums.friend.ImFriendAddSourceEnum}
     */
    private Integer addSource;
    /**
     * æ˜¯å¦ç½®é¡¶è”系人
     */
    private Boolean pinned;
    /**
     * æ˜¯å¦æ‹‰é»‘(弱关联 friend,单边屏蔽对方私聊消息)
     */
    private Boolean blocked;
    /**
     * å¥½å‹çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link CommonStatusEnum}
     */
    private Integer status;
    /**
     * æ·»åŠ å¥½å‹æ—¶é—´
     */
    private LocalDateTime addTime;
    /**
     * åˆ é™¤å¥½å‹æ—¶é—´
     * <p>
     * ä¸ä¸º null æ—¶è¡¨ç¤ºå·²åˆ é™¤
     */
    private LocalDateTime deleteTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/friend/ImFriendRequestDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,85 @@
package cn.iocoder.yudao.module.im.dal.dataobject.friend;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.enums.friend.ImFriendAddSourceEnum;
import cn.iocoder.yudao.module.im.enums.friend.ImFriendRequestHandleResultEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * IM å¥½å‹ç”³è¯·è®°å½• DO
 * <p>
 * é…åˆã€Œç”³è¯· - å®¡æ‰¹ã€æµç¨‹ï¼š
 * - å‘起方调 apply æŽ¥å£è½åº“(handleResult=UNHANDLED)
 * - æŽ¥æ”¶æ–¹è°ƒ agree / refuse å¤„理(更新 handleResult / handleTime / handleContent)
 * - ç”³è¯·é€šè¿‡åŽï¼ŒdisplayName / addSource åŒæ­¥å†™å…¥ {@link ImFriendDO}
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_friend_request")
@KeySequence("im_friend_request_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImFriendRequestDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å‘起方用户编号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long fromUserId;
    /**
     * æŽ¥æ”¶æ–¹ç”¨æˆ·ç¼–号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long toUserId;
    // ========== ç”³è¯·å‘起阶段(发起方填写) ==========
    /**
     * ç”³è¯·ç†ç”±
     */
    private String applyContent;
    /**
     * å‘起方对接收方的备注
     * <p>
     * åŒæ„åŽå†™å…¥ A ä¾§ {@link ImFriendDO#getDisplayName()};B ä¾§ä¸åЍ
     */
    private String displayName;
    /**
     * æ·»åŠ æ¥æº
     * <p>
     * æžšä¸¾ {@link ImFriendAddSourceEnum}
     */
    private Integer addSource;
    // ========== ç”³è¯·å¤„理阶段(接收方填写) ==========
    /**
     * å¤„理结果
     * <p>
     * æžšä¸¾ {@link ImFriendRequestHandleResultEnum}
     */
    private Integer handleResult;
    /**
     * å¤„理理由(接收方拒绝时可选填)
     */
    private String handleContent;
    /**
     * å¤„理时间
     */
    private LocalDateTime handleTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,94 @@
package cn.iocoder.yudao.module.im.dal.dataobject.group;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
import java.util.List;
/**
 * IM ç¾¤ä¿¡æ¯ DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName(value = "im_group",autoResultMap = true)
@KeySequence("im_group_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增。如果是 MySQL ç­‰æ•°æ®åº“,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImGroupDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç¾¤åç§°
     */
    private String name;
    /**
     * ç¾¤ä¸»ç”¨æˆ·ç¼–号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long ownerUserId;
    /**
     * ç¾¤å¤´åƒ
     */
    private String avatar;
    /**
     * ç¾¤å…¬å‘Š
     */
    private String notice;
    /**
     * è¿›ç¾¤æ˜¯å¦éœ€ç¾¤ä¸» / ç®¡ç†å‘˜å®¡æ‰¹
     * <p>
     * false è‡ªç”±è¿›ç¾¤ï¼ˆé»˜è®¤ï¼‰ï¼›true éœ€å®¡æ‰¹ï¼Œæ‰€æœ‰ã€Œç”³è¯·ã€ã€Œé‚€è¯·ã€è·¯å¾„都需群主 / ç®¡ç†å‘˜åŒæ„
     */
    private Boolean joinApproval;
    /**
     * æ˜¯å¦å°ç¦
     */
    private Boolean banned;
    /**
     * å°ç¦åŽŸå› 
     */
    private String bannedReason;
    /**
     * å°ç¦æ—¶é—´
     */
    private LocalDateTime bannedTime;
    /**
     * ç¾¤çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link CommonStatusEnum}
     * ENABLE = æ­£å¸¸ï¼ŒDISABLE = å·²è§£æ•£
     */
    private Integer status;
    /**
     * è§£æ•£æ—¶é—´
     */
    private LocalDateTime dissolvedTime;
    /**
     * æ˜¯å¦å…¨ç¾¤ç¦è¨€
     */
    private Boolean mutedAll;
    /**
     * ç¾¤ç½®é¡¶æ¶ˆæ¯ç¼–号列表
     * <p>
     * ä»…å­˜ messageId,操作人 / ç½®é¡¶æ—¶é—´ä»Žå¯¹åº” PIN äº‹ä»¶çš„æ¶ˆæ¯è®°å½•里反查
     */
    @TableField(typeHandler = LongListTypeHandler.class)
    private List<Long> pinnedMessageIds;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupMemberDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,102 @@
package cn.iocoder.yudao.module.im.dal.dataobject.group;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.module.im.enums.group.ImGroupAddSourceEnum;
import cn.iocoder.yudao.module.im.enums.group.ImGroupMemberRoleEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * IM ç¾¤æˆå‘˜ DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_group_member")
@KeySequence("im_group_member_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增。如果是 MySQL ç­‰æ•°æ®åº“,可不写。
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImGroupMemberDO extends BaseDO {
    /**
     * æ°¸ä¹…禁言到期时间
     */
    public static final LocalDateTime PERMANENT_MUTE_END_TIME = LocalDateTime.of(9999, 12, 31, 23, 59, 59);
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç¾¤ç¼–号
     * <p>
     * å…³è” {@link ImGroupDO#getId()}
     */
    private Long groupId;
    /**
     * ç”¨æˆ·ç¼–号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long userId;
    /**
     * ç»„内显示名
     */
    private String displayUserName;
    /**
     * ç¾¤å¤‡æ³¨
     */
    private String groupRemark;
    /**
     * æ˜¯å¦å…æ‰“扰
     */
    private Boolean silent;
    /**
     * æˆå‘˜çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link CommonStatusEnum}
     */
    private Integer status;
    /**
     * æˆå‘˜è§’色
     * <p>
     * æžšä¸¾ {@link ImGroupMemberRoleEnum}
     */
    private Integer role;
    /**
     * å…¥ç¾¤æ—¶é—´
     */
    private LocalDateTime joinTime;
    /**
     * åŠ å…¥æ¥æº
     * <p>
     * æžšä¸¾ {@link ImGroupAddSourceEnum}
     */
    private Integer addSource;
    /**
     * é‚€è¯·äººç”¨æˆ·ç¼–号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段;用户主动申请进群时为 NULL
     */
    private Long inviterUserId;
    /**
     * é€€ç¾¤æ—¶é—´
     */
    private LocalDateTime quitTime;
    /**
     * ç¦è¨€åˆ°æœŸæ—¶é—´
     * <p>
     * null è¡¨ç¤ºæœªç¦è¨€ï¼›éž null ä¸”在未来表示禁言中;
     */
    private LocalDateTime muteEndTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/group/ImGroupRequestDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,91 @@
package cn.iocoder.yudao.module.im.dal.dataobject.group;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.enums.group.ImGroupAddSourceEnum;
import cn.iocoder.yudao.module.im.enums.group.ImGroupRequestHandleResultEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * IM åŠ ç¾¤ç”³è¯·è®°å½• DO
 * <p>
 * é…åˆã€Œç”³è¯· - å®¡æ‰¹ã€æµç¨‹ï¼š
 * <ul>
 *     <li>用户主动申请:调 apply æŽ¥å£è½åº“(inviterUserId=null,handleResult=UNHANDLED),群主 / ç®¡ç†å‘˜å®¡æ‰¹</li>
 *     <li>普通成员邀请:群 joinType=APPLY_AND_NORMAL_INVITE æ—¶è½åº“(inviterUserId=操作人),群主 / ç®¡ç†å‘˜å®¡æ‰¹</li>
 *     <li>处理:群主 / ç®¡ç†å‘˜è°ƒ agree / refuse æŽ¨è¿›çŠ¶æ€æœºï¼›åŒæ„æ—¶æŠŠ addSource / inviterUserId åŒæ­¥å†™å…¥ {@link ImGroupMemberDO}</li>
 * </ul>
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_group_request")
@KeySequence("im_group_request_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增;MySQL ç­‰å¯ä¸å†™
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImGroupRequestDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ç¾¤ç¼–号
     * <p>
     * å…³è” {@link ImGroupDO#getId()}
     */
    private Long groupId;
    /**
     * ç”³è¯·äºº / è¢«é‚€è¯·äººç”¨æˆ·ç¼–号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long userId;
    /**
     * é‚€è¯·äººç”¨æˆ·ç¼–号
     * <p>
     * NULL è¡¨ç¤ºç”¨æˆ·ä¸»åŠ¨ç”³è¯·è¿›ç¾¤ï¼›éž NULL è¡¨ç¤ºç”±ç¾¤æˆå‘˜é‚€è¯·ã€å¾…群主 / ç®¡ç†å‘˜å®¡æ‰¹
     */
    private Long inviterUserId;
    // ========== ç”³è¯·å‘起阶段 ==========
    /**
     * ç”³è¯·ç†ç”±
     */
    private String applyContent;
    /**
     * åŠ å…¥æ¥æº
     * <p>
     * æžšä¸¾ {@link ImGroupAddSourceEnum};同意时同步写入 {@link ImGroupMemberDO#getAddSource()}
     */
    private Integer addSource;
    // ========== ç”³è¯·å¤„理阶段 ==========
    /**
     * å¤„理结果
     * <p>
     * æžšä¸¾ {@link ImGroupRequestHandleResultEnum}
     */
    private Integer handleResult;
    /**
     * å¤„理人用户编号(群主或管理员)
     */
    private Long handleUserId;
    /**
     * å¤„理理由(拒绝时可选填)
     */
    private String handleContent;
    /**
     * å¤„理时间
     */
    private LocalDateTime handleTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImChannelMessageDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,74 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
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.enums.ImContentTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
import java.util.List;
/**
 * IM é¢‘道消息 DO
 * <p>
 * ä¸šåŠ¡è¯­ä¹‰ï¼š
 * - ä¸€æ¬¡æŽ¨é€ 1 è¡Œï¼›{@link #receiverUserIds} ä¸ºç©ºè¡¨ç¤ºå…¨å‘˜
 * - {@link #channelId} å†—ä½™ {@link ImChannelMaterialDO#getChannelId()} ä¾¿äºŽæŒ‰é¢‘道检索
 * - {@link #content} å­˜æŽ¨é€æ—¶ payload çš„ JSON å¿«ç…§ï¼ˆtitle / coverUrl / summary / url);不含富文本正文
 *
 * @author èŠ‹é“æºç 
 */
@TableName(value = "im_channel_message", autoResultMap = true)
@KeySequence("im_channel_message_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImChannelMessageDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * é¢‘道编号
     * <p>
     * å…³è” {@link ImChannelDO#getId()};冗余便于按频道检索
     */
    private Long channelId;
    /**
     * å…³è”素材编号
     * <p>
     * å…³è” {@link ImChannelMaterialDO#getId()}
     */
    private Long materialId;
    /**
     * æ¶ˆæ¯ç±»åž‹
     * <p>
     * æžšä¸¾ {@link ImContentTypeEnum}
     */
    private Integer type;
    /**
     * æ¶ˆæ¯å†…容;推送时 payload çš„ JSON å¿«ç…§
     */
    private String content;
    /**
     * æŽ¥æ”¶äººç¼–号列表;为空表示全员
     */
    @TableField(typeHandler = LongListTypeHandler.class)
    private List<Long> receiverUserIds;
    /**
     * å‘送时间
     */
    private LocalDateTime sendTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImGroupMessageDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,107 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.framework.mybatis.core.type.LongListTypeHandler;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.enums.message.ImMessageReceiptStatusEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
import cn.iocoder.yudao.module.im.enums.ImContentTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
import java.util.List;
/**
 * IM ç¾¤èŠæ¶ˆæ¯ DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName(value = "im_group_message", autoResultMap = true)
@KeySequence("im_group_message_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImGroupMessageDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å®¢æˆ·ç«¯æ¶ˆæ¯ç¼–号,用于发送幂等
     */
    private String clientMessageId;
    /**
     * å‘送人编号
     *
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long senderId;
    /**
     * ç¾¤ç¼–号
     *
     * å…³è” {@link ImGroupDO#getId()}
     */
    private Long groupId;
    /**
     * æ¶ˆæ¯ç±»åž‹
     * <p>
     * æžšä¸¾ {@link ImContentTypeEnum}
     */
    private Integer type;
    /**
     * æ¶ˆæ¯å†…容,JSON æ ¼å¼
     *
     * å‚考 content åŒ…下的 TextMessage、ImageMessage ç­‰ç»“构化模型
     */
    private String content;
    /**
     * æ¶ˆæ¯çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link ImMessageStatusEnum}
     */
    private Integer status;
    /**
     * å‘送时间
     */
    private LocalDateTime sendTime;
    /**
     * å®šå‘接收用户编号列表,以逗号分隔
     * <p>
     * ä¸ºç©ºè¡¨ç¤ºå…¨å‘˜å¯è§
     *
     * å…³è” AdminUserDO çš„ id å­—段
     */
    @TableField(typeHandler = LongListTypeHandler.class)
    private List<Long> receiverUserIds;
    /**
     * @ ç›®æ ‡ç”¨æˆ·ç¼–号列表,以逗号分隔
     *
     * å…³è” AdminUserDO çš„ id å­—段
     */
    @TableField(typeHandler = LongListTypeHandler.class)
    private List<Long> atUserIds;
    /**
     * å›žæ‰§çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link ImMessageReceiptStatusEnum}
     */
    private Integer receiptStatus;
    // ========== éžè¡¨å­—段 ==========
    /**
     * ç¦»çº¿æ‹‰å–等场景下回算的已读人数
     */
    @TableField(exist = false)
    private Integer readCount;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/ImPrivateMessageDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,79 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.enums.message.ImMessageReceiptStatusEnum;
import cn.iocoder.yudao.module.im.enums.message.ImMessageStatusEnum;
import cn.iocoder.yudao.module.im.enums.ImContentTypeEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * IM ç§èŠæ¶ˆæ¯ DO
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_private_message")
@KeySequence("im_private_message_seq")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImPrivateMessageDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * å®¢æˆ·ç«¯æ¶ˆæ¯ç¼–号,用于发送幂等
     */
    private String clientMessageId;
    /**
     * å‘送人编号
     *
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long senderId;
    /**
     * æŽ¥æ”¶äººç¼–号
     *
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long receiverId;
    /**
     * æ¶ˆæ¯ç±»åž‹
     * <p>
     * æžšä¸¾ {@link ImContentTypeEnum}
     */
    private Integer type;
    /**
     * æ¶ˆæ¯å†…容,JSON æ ¼å¼
     *
     * å‚考 content åŒ…下的 TextMessage、ImageMessage ç­‰ç»“构化模型
     */
    private String content;
    /**
     * æ¶ˆæ¯çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link ImMessageStatusEnum}
     */
    private Integer status;
    /**
     * å›žæ‰§çŠ¶æ€
     * <p>
     * æžšä¸¾ {@link ImMessageReceiptStatusEnum}
     */
    private Integer receiptStatus;
    /**
     * å‘送时间
     */
    private LocalDateTime sendTime;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/AudioMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,29 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
/**
 * è¯­éŸ³æ¶ˆæ¯å†…容
 */
@Data
public class AudioMessage {
    /**
     * è¯­éŸ³æ–‡ä»¶ URL
     */
    private String url;
    /**
     * è¯­éŸ³æ—¶é•¿ï¼ˆç§’)
     */
    private Integer duration;
    /**
     * æ–‡ä»¶å¤§å°ï¼ˆå­—节)
     */
    private Long size;
    /**
     * å¼•用消息(可选)
     */
    private QuoteMessage quote;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/CardMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,44 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.enums.ImConversationTypeEnum;
import lombok.Data;
/**
 * åç‰‡æ¶ˆæ¯å†…容
 * <p>
 * æ”¯æŒä¸¤ç±»åç‰‡ï¼ŒæŒ‰ targetType åŒºåˆ†ï¼š
 * 1. ç”¨æˆ·åç‰‡ï¼ˆtargetType = PRIVATE):targetId = ç”¨æˆ·ç¼–号,name = ç”¨æˆ·æ˜µç§°
 * 2. ç¾¤åç‰‡ï¼ˆtargetType = GROUP):targetId = ç¾¤ç¼–号,name = ç¾¤åï¼Œå¯å¸¦ memberCount
 */
@Data
public class CardMessage {
    /**
     * åç‰‡å¯¹è±¡ç±»åž‹
     * <p>
     * æžšä¸¾ {@link ImConversationTypeEnum}
     */
    private Integer targetType;
    /**
     * ç›®æ ‡å¯¹è±¡ç¼–号:PRIVATE æ—¶ = ç”¨æˆ·ç¼–号;GROUP æ—¶ = ç¾¤ç¼–号
     */
    private Long targetId;
    /**
     * æ˜¾ç¤ºåå¿«ç…§ï¼šPRIVATE æ—¶ = ç”¨æˆ·æ˜µç§°ï¼›GROUP æ—¶ = ç¾¤å
     */
    private String name;
    /**
     * å¤´åƒï¼ˆå¿«ç…§ï¼‰
     */
    private String avatar;
    /**
     * ç¾¤æˆå‘˜æ•°ï¼ˆä»… targetType = GROUP;接收端展示「N äººç¾¤èŠã€ï¼‰
     */
    private Integer memberCount;
    /**
     * å¼•用消息(可选)
     */
    private QuoteMessage quote;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FaceMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,39 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
/**
 * è¡¨æƒ…消息内容
 * <p>
 * æ‰¿è½½ã€Œè´´å›¾ / è‡ªå®šä¹‰è¡¨æƒ…包」类消息;Unicode emoji(😀😂)仍走 TEXT
 */
@Data
public class FaceMessage {
    /**
     * è¡¨æƒ…图 URL
     */
    private String url;
    /**
     * æ¸²æŸ“宽度(像素,可选)
     * <p>
     * å®¢æˆ·ç«¯æ¸²æŸ“时按此尺寸占位,避免图片加载完成后的布局抖动;缺失时由客户端 CSS max-width å…œåº•
     */
    private Integer width;
    /**
     * æ¸²æŸ“高度(像素,可选)
     */
    private Integer height;
    /**
     * è¡¨æƒ…名(可选)
     * <p>
     * ç³»ç»Ÿè¡¨æƒ…包通常带名字(如「狗头」「捂脸」),用户私有表情包通常为空
     */
    private String name;
    /**
     * å¼•用消息(可选)
     */
    private QuoteMessage quote;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/FileMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,33 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
/**
 * æ–‡ä»¶æ¶ˆæ¯å†…容
 */
@Data
public class FileMessage {
    /**
     * æ–‡ä»¶ URL
     */
    private String url;
    /**
     * æ–‡ä»¶å
     */
    private String name;
    /**
     * æ–‡ä»¶å¤§å°ï¼ˆå­—节)
     */
    private Long size;
    /**
     * æ–‡ä»¶ç±»åž‹ï¼ˆæ‰©å±•名)
     */
    private String type;
    /**
     * å¼•用消息(可选)
     */
    private QuoteMessage quote;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/ImageMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,37 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
/**
 * å›¾ç‰‡æ¶ˆæ¯å†…容
 */
@Data
public class ImageMessage {
    /**
     * å›¾ç‰‡ URL
     */
    private String url;
    /**
     * ç¼©ç•¥å›¾ URL
     */
    private String thumbnailUrl;
    /**
     * å›¾ç‰‡å®½åº¦
     */
    private Integer width;
    /**
     * å›¾ç‰‡é«˜åº¦
     */
    private Integer height;
    /**
     * å›¾ç‰‡å¤§å°ï¼ˆå­—节)
     */
    private Long size;
    /**
     * å¼•用消息(可选)
     */
    private QuoteMessage quote;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/LocationMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,24 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
/**
 * ä½ç½®æ¶ˆæ¯å†…容
 */
@Data
public class LocationMessage {
    /**
     * ä½ç½®æè¿°
     */
    private String description;
    /**
     * ç»åº¦
     */
    private Double longitude;
    /**
     * çº¬åº¦
     */
    private Double latitude;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/MaterialMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,51 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
import lombok.experimental.Accessors;
/**
 * é¢‘道素材消息 payload
 * <p>
 * å¯¹åº” {@link cn.iocoder.yudao.module.im.enums.ImContentTypeEnum#MATERIAL}(type=125)。
 * å®¢æˆ·ç«¯æŒ‰æœ¬å­—段集渲染图文卡片;
 * å¯Œæ–‡æœ¬æ­£æ–‡ä¸åœ¨æœ¬ payload ä¸­ä¼ é€’,点击详情时另调 /im/channel/material/get-content?id= æŒ‰éœ€æ‹‰å–。
 */
@Data
@Accessors(chain = true)
public class MaterialMessage {
    /**
     * ç´ æç¼–号
     */
    private Long materialId;
    /**
     * é¢‘道编号
     * <p>
     * å†—余到 payload å†…;转发到私聊 / ç¾¤èŠåŽå®¢æˆ·ç«¯ç”¨æœ¬å­—段定位频道,渲染卡片底部的频道头像 + åç§°
     */
    private Long channelId;
    /**
     * ç´ æå†…容类型
     * <p>
     * æžšä¸¾ {@link cn.iocoder.yudao.module.im.enums.channel.ImChannelMaterialTypeEnum}
     * å®¢æˆ·ç«¯æŒ‰æœ¬å­—段判定点击行为:CONTENT èµ°ç«™å†…详情页拉富文本;LINK è·³ url
     */
    private Integer type;
    /**
     * æ ‡é¢˜
     */
    private String title;
    /**
     * å°é¢å›¾
     */
    private String coverUrl;
    /**
     * æ‘˜è¦
     */
    private String summary;
    /**
     * è·³è½¬é“¾æŽ¥
     */
    private String url;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/MergeMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,64 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.enums.ImContentTypeEnum;
import lombok.Data;
import java.util.List;
/**
 * åˆå¹¶è½¬å‘消息内容
 * <p>
 * payload å†…嵌完整快照,原消息撤回 / åˆ é™¤ä¸å½±å“å·²è½¬å‘的合并消息
 */
@Data
public class MergeMessage {
    /**
     * åˆå¹¶æ ‡é¢˜
     */
    private String title;
    /**
     * å†…嵌的完整消息快照
     */
    private List<Item> messages;
    /**
     * å•条内嵌消息快照
     */
    @Data
    public static class Item {
        /**
         * åŽŸæ¶ˆæ¯ç¼–å·
         */
        private Long messageId;
        /**
         * å‘送人编号
         */
        private Long senderId;
        /**
         * å‘送人昵称快照;接收方可能不在原会话里,无法实时查到
         */
        private String senderNickname;
        /**
         * å‘送人头像快照
         */
        private String senderAvatar;
        /**
         * æ¶ˆæ¯ç±»åž‹
         * <p>
         * æžšä¸¾ {@link ImContentTypeEnum}
         */
        private Integer type;
        /**
         * åŽŸæ¶ˆæ¯ content(JSON)
         */
        private String content;
        /**
         * å‘送时间戳(毫秒)
         */
        private Long sendTime;
    }
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/QuoteMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,50 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import cn.iocoder.yudao.module.im.enums.ImContentTypeEnum;
import lombok.Data;
import lombok.experimental.Accessors;
/**
 * å¼•用消息内容
 * <p>
 * å®¢æˆ·ç«¯åœ¨ content.quote é‡Œå†™å…¥ messageId è¡¨è¾¾"引用了哪条消息",服务端反查原消息后重算 QuoteMessage è¦†ç›–客户端写入,
 * é¿å…ä¼ªé€ å‘送人 / ç±»åž‹ / æ‘˜è¦
 *
 * @see TextMessage ç­‰ç­‰æ¶ˆæ¯å†…容里 quote å­—段
 */
@Data
@Accessors(chain = true)
public class QuoteMessage {
    /**
     * content JSON é‡Œ quote å­—段名
     */
    public static final String FIELD_NAME = "quote";
    /**
     * QuoteMessage é‡Œ messageId å­—段名
     */
    public static final String FIELD_MESSAGE_ID = "messageId";
    /**
     * è¢«å¼•用消息编号
     */
    private Long messageId;
    /**
     * è¢«å¼•用消息发送人编号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long senderId;
    /**
     * è¢«å¼•用消息类型
     * <p>
     * æžšä¸¾ {@link ImContentTypeEnum}
     */
    private Integer type;
    /**
     * åŽŸæ¶ˆæ¯å®Œæ•´ content(JSON);服务端复制时会 removeQuote é˜²åµŒå¥—
     */
    private String content;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/RecallMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,18 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
import lombok.experimental.Accessors;
/**
 * æ’¤å›žæç¤ºæ¶ˆæ¯å†…容
 */
@Data
@Accessors(chain = true)
public class RecallMessage {
    /**
     * è¢«æ’¤å›žçš„原消息编号
     */
    private Long messageId;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/TextMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,21 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
/**
 * æ–‡æœ¬æ¶ˆæ¯å†…容
 */
@Data
public class TextMessage {
    /**
     * æ–‡æœ¬å†…容
     */
    private String content;
    /**
     * å¼•用消息(可选)
     */
    private QuoteMessage quote;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/message/content/VideoMessage.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,41 @@
package cn.iocoder.yudao.module.im.dal.dataobject.message.content;
import lombok.Data;
/**
 * è§†é¢‘消息内容
 */
@Data
public class VideoMessage {
    /**
     * è§†é¢‘文件 URL
     */
    private String url;
    /**
     * è§†é¢‘封面 URL
     */
    private String coverUrl;
    /**
     * è§†é¢‘时长(秒)
     */
    private Integer duration;
    /**
     * è§†é¢‘宽度
     */
    private Integer width;
    /**
     * è§†é¢‘高度
     */
    private Integer height;
    /**
     * æ–‡ä»¶å¤§å°ï¼ˆå­—节)
     */
    private Long size;
    /**
     * å¼•用消息(可选)
     */
    private QuoteMessage quote;
}
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/rtc/ImRtcCallDO.java
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,93 @@
package cn.iocoder.yudao.module.im.dal.dataobject.rtc;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import cn.iocoder.yudao.module.im.dal.dataobject.group.ImGroupDO;
import cn.iocoder.yudao.module.im.enums.ImConversationTypeEnum;
import cn.iocoder.yudao.module.im.enums.rtc.ImRtcCallEndReasonEnum;
import cn.iocoder.yudao.module.im.enums.rtc.ImRtcCallMediaTypeEnum;
import cn.iocoder.yudao.module.im.enums.rtc.ImRtcCallStatusEnum;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
import java.time.LocalDateTime;
/**
 * IM é€šè¯è®°å½• DO(房间级 / ä¸»è¡¨ï¼‰
 * <p>
 * ä¸€é€šé€šè¯ä¸€è¡Œï¼›çŠ¶æ€æœº CREATED â†’ RUNNING â†’ ENDED;和明细表 {@link ImRtcParticipantDO} é€šè¿‡ {@link #room} å…³è”
 *
 * @author èŠ‹é“æºç 
 */
@TableName("im_rtc_call")
@KeySequence("im_rtc_call_seq") // ç”¨äºŽ Oracle、PostgreSQL、Kingbase、DB2、H2 æ•°æ®åº“的主键自增;MySQL ç­‰æ•°æ®åº“可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ImRtcCallDO extends BaseDO {
    /**
     * ç¼–号
     */
    @TableId
    private Long id;
    /**
     * ä¸šåŠ¡é€šè¯ç¼–å·ï¼ˆUUID,同时作为 LiveKit æˆ¿é—´åï¼‰ï¼›å”¯ä¸€
     */
    private String room;
    /**
     * ä¼šè¯ç±»åž‹
     *
     * æžšä¸¾ {@link ImConversationTypeEnum}
     */
    private Integer conversationType;
    /**
     * åª’体类型
     *
     * æžšä¸¾ {@link ImRtcCallMediaTypeEnum}
     */
    private Integer mediaType;
    /**
     * å‘起人用户编号
     * <p>
     * å…³è” AdminUserDO çš„ id å­—段
     */
    private Long inviterUserId;
    /**
     * ç¾¤ç¼–号;私聊为 NULL
     * <p>
     * å…³è” {@link ImGroupDO#getId()}
     */
    private Long groupId;
    /**
     * é€šè¯çŠ¶æ€
     *
     * æžšä¸¾ {@link ImRtcCallStatusEnum}
     */
    private Integer status;
    /**
     * ç»“束原因;通话未结束时为 NULL
     *
     * æžšä¸¾ {@link ImRtcCallEndReasonEnum}
     */
    private Integer endReason;
    /**
     * å‘èµ·æ—¶é—´
     */
    private LocalDateTime startTime;
    /**
     * æŽ¥é€šæ—¶é—´
     *
     * é¦–个非发起人加入时写入;未接通保持 NULL
     */
    private LocalDateTime acceptTime;
    /**
     * é€šè¯ç»“束时间
     */
    private LocalDateTime endTime;
}
在上述文件截断后对比
yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/rtc/ImRtcParticipantDO.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/dataobject/sensitiveword/ImSensitiveWordDO.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/channel/ImChannelMaterialMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationReadMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFacePackItemMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFacePackMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/face/ImFaceUserItemMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendRequestMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupMemberMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/group/ImGroupRequestMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImChannelMessageMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImGroupMessageMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImPrivateMessageMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/rtc/ImRtcCallMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/rtc/ImRtcParticipantMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/sensitiveword/ImSensitiveWordMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapper.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/redis/RedisKeyConstants.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/dal/redis/rtc/ImRtcCallLockRedisDAO.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ErrorCodeConstants.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ImCommonConstants.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ImContentTypeEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/ImConversationTypeEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/channel/ImChannelMaterialTypeEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/friend/ImFriendAddSourceEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/friend/ImFriendRequestHandleResultEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/friend/ImFriendStateEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/group/ImGroupAddSourceEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/group/ImGroupMemberRoleEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/group/ImGroupRequestHandleResultEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageReceiptStatusEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/message/ImMessageStatusEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcCallEndReasonEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcCallMediaTypeEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcCallStatusEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcParticipantRoleEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/enums/rtc/ImRtcParticipantStatusEnum.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/config/ImProperties.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/package-info.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/config/ImRtcConfiguration.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitClient.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/rtc/core/LiveKitWebhookEventDTO.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/web/config/ImWebConfiguration.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/framework/web/package-info.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/job/rtc/ImRtcCallCleanupJob.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/job/rtc/ImRtcParticipantTimeoutJob.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/mq/consumer/friend/AdminUserProfileUpdateConsumer.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/package-info.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelMaterialServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/channel/ImChannelServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationReadService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/conversation/ImConversationReadServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackItemService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackItemServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFacePackServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFaceUserItemService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/face/ImFaceUserItemServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendRequestService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendRequestServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/dto/ImGroupMessageSendDTO.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/message/dto/ImPrivateMessageSendDTO.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/sensitiveword/ImSensitiveWordService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/sensitiveword/ImSensitiveWordServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/ImWebSocketService.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/ImWebSocketServiceImpl.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/ImNotificationWebSocketDTO.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/BaseFriendNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendAddNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendBlockNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendDeleteNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendInfoUpdatedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendRequestApprovedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendRequestNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendRequestRejectedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendUnblockNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/friend/FriendUpdateNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/BaseGroupNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupAdminAddNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupAdminRemoveNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupBannedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupCancelMutedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupCreateNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupDissolveNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupInfoUpdateNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberCancelMutedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberEnterNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberInviteNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberKickNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberListNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberMutedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberNicknameUpdateNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberQuitNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMemberSettingUpdateNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMessagePinNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMessageUnpinNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupMutedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupNameUpdateNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupNoticeUpdateNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupOwnerTransferNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupRequestApprovedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupRequestReceivedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/group/GroupRequestRejectedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImChannelMessageNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImGroupMessageNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImMessageReadNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImMessageReceiptNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/message/ImPrivateMessageNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/package-info.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcCallEndNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcCallNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcCallStartNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcParticipantConnectedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/service/websocket/notification/rtc/ImRtcParticipantDisconnectedNotification.java yudao-module-im/src/main/java/cn/iocoder/yudao/module/im/util/ImMessageUtils.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/conversation/ImConversationReadMapperTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/friend/ImFriendRequestMapperTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImGroupMessageMapperTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/message/ImPrivateMessageMapperTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/dal/mysql/statistics/ImStatisticsManagerMapperTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/face/ImFacePackItemServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/face/ImFacePackServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/face/ImFaceUserItemServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendRequestServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/friend/ImFriendServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupMemberServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupRequestServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/group/ImGroupServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImChannelMessageServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImGroupMessageServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/message/ImPrivateMessageServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/rtc/ImRtcCallServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/sensitiveword/ImSensitiveWordServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/statistics/ImStatisticsManagerServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/service/websocket/ImWebSocketServiceImplTest.java yudao-module-im/src/test/java/cn/iocoder/yudao/module/im/util/ImMessageUtilsTest.java yudao-module-im/src/test/resources/application-unit-test.yaml yudao-module-im/src/test/resources/sql/clean.sql yudao-module-im/src/test/resources/sql/create_tables.sql yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/FileController.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/admin/file/vo/file/FileUploadReqVO.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/controller/app/file/AppFileController.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/dal/mysql/file/FileMapper.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/ErrorCodeConstants.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/enums/codegen/CodegenFrontTypeEnum.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/AbstractFileClient.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/local/LocalFileClient.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/client/s3/S3FileClient.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FilePathUtils.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtils.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngine.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileService.java yudao-module-infra/src/main/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImpl.java yudao-module-infra/src/main/resources/codegen/java/service/serviceImpl.vm yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/components/search-form.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/detail/index.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/form/index.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_admin_uniapp/views/index.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/api/api.ts.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/form.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/import.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/index.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/form_sub_erp.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/form_sub_inner.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/form_sub_normal.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/list_sub_erp.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/general/views/modules/list_sub_inner.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/api/api.ts.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/data.ts.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/form.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/import.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/index.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/form_sub_erp.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/form_sub_inner.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/form_sub_normal.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/list_sub_erp.vue.vm yudao-module-infra/src/main/resources/codegen/vue3_vben5_antdv_next/schema/views/modules/list_sub_inner.vue.vm yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/local/LocalFileClientTest.java yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/s3/S3FileClientTest.java yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/framework/file/core/utils/FileTypeUtilsTest.java yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineAbstractTest.java yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/codegen/inner/CodegenEngineUniappTest.java yudao-module-infra/src/test/java/cn/iocoder/yudao/module/infra/service/file/FileServiceImplTest.java yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_inner/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vben5_antd_general_master_normal/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_inner/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vben5_antd_schema_master_normal/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_inner/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vben5_ele_general_master_normal/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_inner/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vben5_ele_schema_master_normal/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vue2_master_inner/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vue2_master_normal/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/assert.json yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/ErrorCodeConstants_手动操作 yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentController yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentDO yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentMapper yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentPageReqVO yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentRespVO yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentSaveReqVO yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentService yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/java/InfraStudentServiceImplTest yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/sql/h2 yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/sql/sql yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/ts/index yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/detail/index yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/form/index yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/index yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/vue/search-form yudao-module-infra/src/test/resources/codegen/vue3_admin_uniapp_one/xml/InfraStudentMapper yudao-module-infra/src/test/resources/codegen/vue3_master_inner/java/InfraStudentServiceImpl yudao-module-infra/src/test/resources/codegen/vue3_master_normal/java/InfraStudentServiceImpl yudao-module-iot/yudao-module-iot-biz/pom.xml yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/api/device/IoTDeviceApiImpl.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigRespVO.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/alert/vo/config/IotAlertConfigSaveReqVO.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/controller/admin/rule/vo/scene/IotSceneRuleRespVO.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/dataobject/alert/IotAlertConfigDO.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/alert/IotAlertConfigMapper.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/device/IotDeviceMapper.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/dal/mysql/rule/IotDataRuleMapper.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/enums/ErrorCodeConstants.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableField.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/alert/IotAlertConfigServiceImpl.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImpl.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimeHelper.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleAction.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/IotSceneRuleMatcherHelper.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotCurrentTimeConditionMatcher.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDevicePropertyConditionMatcher.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/condition/IotDeviceStateConditionMatcher.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceEventPostTriggerMatcher.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDevicePropertyPostTriggerMatcher.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceServiceInvokeTriggerMatcher.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/rule/scene/matcher/trigger/IotDeviceStateUpdateTriggerMatcher.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelService.java yudao-module-iot/yudao-module-iot-biz/src/main/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImpl.java yudao-module-iot/yudao-module-iot-biz/src/main/resources/mapper/device/IotDevicePropertyMapper.xml yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/framework/tdengine/core/TDengineTableFieldTest.java yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/device/property/IotDevicePropertyServiceImplTest.java yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/IotSceneRuleTimerConditionIntegrationTest.java yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/rule/scene/action/IotAlertTriggerSceneRuleActionTest.java yudao-module-iot/yudao-module-iot-biz/src/test/java/cn/iocoder/yudao/module/iot/service/thingmodel/IotThingModelServiceImplTest.java yudao-module-iot/yudao-module-iot-core/pom.xml yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusAutoConfiguration.java yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/config/IotMessageBusProperties.java yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/kafka/IotKafkaMessageBus.java yudao-module-iot/yudao-module-iot-core/src/main/java/cn/iocoder/yudao/module/iot/core/messagebus/core/rabbitmq/IotRabbitMQMessageBus.java yudao-module-iot/yudao-module-iot-gateway/pom.xml yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/manager/AbstractIotModbusPollScheduler.java yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/common/utils/IotModbusTcpClientUtils.java yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/manager/IotModbusTcpClientPollScheduler.java yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/IotModbusTcpServerProtocol.java yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/manager/IotModbusTcpServerConfigCacheService.java yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpserver/manager/IotModbusTcpServerConnectionManager.java yudao-module-iot/yudao-module-iot-gateway/src/main/java/cn/iocoder/yudao/module/iot/gateway/service/device/remote/IotDeviceApiImpl.java yudao-module-iot/yudao-module-iot-gateway/src/main/resources/application.yaml yudao-module-iot/yudao-module-iot-gateway/src/test/java/cn/iocoder/yudao/module/iot/gateway/protocol/modbus/tcpclient/manager/IotModbusTcpClientPollSchedulerTest.java yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/comment/ProductCommentController.java yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/controller/admin/spu/ProductSpuController.java yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/dal/mysql/spu/ProductSpuMapper.java yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentService.java yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/comment/ProductCommentServiceImpl.java yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuService.java yudao-module-mall/yudao-module-product/src/main/java/cn/iocoder/yudao/module/product/service/spu/ProductSpuServiceImpl.java yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/coupon/CouponController.java yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/coupon/CouponTemplateMapper.java yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/reward/RewardActivityMapper.java yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/dal/mysql/seckill/seckillactivity/SeckillActivityMapper.java yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImpl.java yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponService.java yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponServiceImpl.java yudao-module-mall/yudao-module-promotion/src/main/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImpl.java yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/combination/CombinationRecordServiceImplTest.java yudao-module-mall/yudao-module-promotion/src/test/java/cn/iocoder/yudao/module/promotion/service/coupon/CouponTemplateServiceImplTest.java yudao-module-mall/yudao-module-promotion/src/test/resources/sql/clean.sql yudao-module-mall/yudao-module-promotion/src/test/resources/sql/create_tables.sql yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/DeliveryPickUpStoreController.java yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/admin/delivery/vo/pickup/DeliveryPickUpStoreRespVO.java yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/controller/app/aftersale/AppAfterSaleController.java yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/convert/delivery/DeliveryPickUpStoreConvert.java yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java yudao-module-mall/yudao-module-trade/src/main/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImpl.java yudao-module-mall/yudao-module-trade/src/main/resources/mapper/brokerage/BrokerageUserMapper.xml yudao-module-mall/yudao-module-trade/src/test/java/cn/iocoder/yudao/module/trade/service/brokerage/BrokerageRecordServiceImplTest.java yudao-module-mall/yudao-module-trade/src/test/resources/sql/create_tables.sql yudao-module-mdm-api/pom.xml (已删除) yudao-module-mdm/pom.xml (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/brand/MdmBrandApi.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/brand/MdmBrandApiImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/brand/dto/MdmBrandRespDTO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApi.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/MdmItemCategoryApiImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/category/dto/MdmItemCategoryRespDTO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/MdmItemApi.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/MdmItemApiImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/MdmItemSkuApi.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/MdmItemSkuApiImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemCreateReqDTO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemRespDTO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemSkuRespDTO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/item/dto/MdmItemUpdateReqDTO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/unit/MdmUnitMeasureApi.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/unit/MdmUnitMeasureApiImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/unit/dto/MdmUnitMeasureRespDTO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/warehouse/MdmWarehouseApi.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/warehouse/MdmWarehouseApiImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/api/warehouse/dto/MdmWarehouseRespDTO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/brand/MdmBrandController.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/brand/vo/MdmBrandPageReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/brand/vo/MdmBrandSaveReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/category/MdmItemCategoryController.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/category/vo/MdmItemCategoryPageReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/category/vo/MdmItemCategoryRespVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/category/vo/MdmItemCategorySaveReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/MdmItemController.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemPageReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemRespVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/item/vo/MdmItemSaveReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/unit/MdmUnitMeasureController.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/unit/vo/MdmUnitMeasurePageReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/unit/vo/MdmUnitMeasureSaveReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/warehouse/MdmWarehouseController.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/warehouse/vo/MdmWarehousePageReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/warehouse/vo/MdmWarehouseRespVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/controller/admin/warehouse/vo/MdmWarehouseSaveReqVO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/brand/MdmBrandDO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/category/MdmItemCategoryDO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/item/MdmItemBatchConfigDO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/item/MdmItemDO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/item/MdmItemSkuDO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/unit/MdmUnitMeasureDO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/dataobject/warehouse/MdmWarehouseDO.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/brand/MdmBrandMapper.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/category/MdmItemCategoryMapper.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/item/MdmItemBatchConfigMapper.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/item/MdmItemMapper.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/item/MdmItemSkuMapper.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/unit/MdmUnitMeasureMapper.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/dal/mysql/warehouse/MdmWarehouseMapper.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/enums/ErrorCodeConstants.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/enums/MdmItemTypeEnum.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/brand/MdmBrandService.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/brand/MdmBrandServiceImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryService.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/category/MdmItemCategoryServiceImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemService.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemServiceImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemSkuService.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/item/MdmItemSkuServiceImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/unit/MdmUnitMeasureService.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/unit/MdmUnitMeasureServiceImpl.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/warehouse/MdmWarehouseService.java (已删除) yudao-module-mdm/src/main/java/cn/iocoder/yudao/module/mdm/service/warehouse/MdmWarehouseServiceImpl.java (已删除) yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/address/vo/AddressRespVO.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserBaseVO.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/admin/user/vo/MemberUserPageReqVO.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserInfoRespVO.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/controller/app/user/vo/AppMemberUserUpdateReqVO.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/convert/address/AddressConvert.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/dataobject/user/MemberUserDO.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/dal/mysql/user/MemberUserMapper.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/enums/ErrorCodeConstants.java yudao-module-member/src/main/java/cn/iocoder/yudao/module/member/service/user/MemberUserServiceImpl.java yudao-module-member/src/test/resources/sql/create_tables.sql yudao-module-mes/pom.xml yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/andon/MesProAndonConfigController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/andon/vo/config/MesProAndonConfigRespVO.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/pro/workorder/MesProWorkOrderController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/qc/defectrecord/MesQcDefectRecordController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/miscreceipt/MesWmMiscReceiptController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/miscreceipt/MesWmMiscReceiptLineController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueDetailController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/outsourceissue/MesWmOutsourceIssueLineController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/packages/MesWmPackageController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/packages/MesWmPackageLineController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesDetailController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/returnsales/MesWmReturnSalesLineController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/controller/admin/wm/sn/MesWmSnController.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/dataobject/pro/workorder/MesProWorkOrderDO.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/qc/template/MesQcTemplateMapper.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/dal/mysql/wm/barcode/MesWmBarcodeMapper.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/ErrorCodeConstants.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/MesBizTypeConstants.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/pro/MesProWorkOrderStatusEnum.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/enums/wm/BarcodeBizTypeEnum.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImpl.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/md/item/MesMdItemServiceImpl.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderService.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/MesProWorkOrderServiceImpl.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/pro/workorder/listener/MesProWorkOrderStatusListener.java (已删除) yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/defectrecord/MesQcDefectRecordService.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/qc/defectrecord/MesQcDefectRecordServiceImpl.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeService.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/barcode/MesWmBarcodeServiceImpl.java yudao-module-mes/src/main/java/cn/iocoder/yudao/module/mes/service/wm/sn/MesWmSnServiceImpl.java yudao-module-mes/src/test/java/cn/iocoder/yudao/module/mes/service/md/autocode/MesMdAutoCodeRecordServiceImplTest.java yudao-module-mp/src/main/java/cn/iocoder/yudao/module/mp/service/handler/user/SubscribeHandler.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/admin/channel/PayChannelController.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/controller/app/wallet/AppPayWalletController.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/convert/channel/PayChannelConvert.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/channel/PayChannelMapper.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/dal/mysql/transfer/PayTransferMapper.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/framework/pay/core/client/impl/weixin/AbstractWxPayClient.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelService.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/service/channel/PayChannelServiceImpl.java yudao-module-pay/src/main/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceImpl.java yudao-module-pay/src/test/java/cn/iocoder/yudao/module/pay/service/transfer/PayTransferServiceTest.java yudao-module-pay/src/test/resources/sql/create_tables.sql yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/message/user/AdminUserProfileUpdateMessage.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApi.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/RoleApiImpl.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/permission/dto/RoleRespDTO.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApi.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/api/user/AdminUserApiImpl.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/MailTemplateController.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/mail/vo/template/MailTemplateSimpleRespVO.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/NotifyTemplateController.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/notify/vo/template/NotifyTemplateSimpleRespVO.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/permission/PermissionController.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/SmsTemplateController.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/sms/vo/template/SmsTemplateSimpleRespVO.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/UserController.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/controller/admin/user/vo/user/UserSimpleRespVO.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailLogMapper.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/mail/MailTemplateMapper.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/notify/NotifyTemplateMapper.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/sms/SmsTemplateMapper.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/dal/mysql/tenant/TenantMapper.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/sms/core/client/impl/QiniuSmsClient.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/social/config/YudaoWxClientConfiguration.java (已删除) yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/weixin/config/YudaoWxClientConfiguration.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/framework/weixin/package-info.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/mq/producer/user/AdminUserProducer.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateService.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImpl.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateService.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImpl.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateService.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImpl.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserService.java yudao-module-system/src/main/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImpl.java yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailLogServiceImplTest.java yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/mail/MailTemplateServiceImplTest.java yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/notify/NotifyTemplateServiceImplTest.java yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/sms/SmsTemplateServiceImplTest.java yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/tenant/TenantServiceImplTest.java yudao-module-system/src/test/java/cn/iocoder/yudao/module/system/service/user/AdminUserServiceImplTest.java yudao-module-wms/pom.xml yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/WmsInventoryApi.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/WmsInventoryApiImpl.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsInventoryQueryReqDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsInventoryRespDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsReceiptReqDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsReceiptResultDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsReserveReqDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsShipmentReqDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsShipmentResultDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/inventory/dto/WmsUseReserveReqDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/stock/WmsStockApi.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/api/stock/dto/StockRecordCreateDTO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/WmsInventoryReserveController.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryReservePageReqVO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/inventory/vo/WmsInventoryReserveRespVO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemListReqVO.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemPageReqVO.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/controller/admin/md/item/vo/item/WmsItemSaveReqVO.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryReserveDO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/inventory/WmsInventoryTransactionDO.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/dataobject/md/item/WmsItemSkuDO.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryMapper.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryReserveMapper.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/dal/mysql/inventory/WmsInventoryTransactionMapper.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/DictTypeConstants.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/ErrorCodeConstants.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsInventoryBizTypeEnum.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsReserveBizTypeEnum.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsReserveStatusEnum.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/inventory/WmsTransactionTypeEnum.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/enums/stock/StockBizTypeEnum.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryReserveService.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryReserveServiceImpl.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryService.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImpl.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryTransactionService.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryTransactionServiceImpl.java (已删除) yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemServiceImpl.java yudao-module-wms/src/main/java/cn/iocoder/yudao/module/wms/service/md/item/WmsItemSkuServiceImpl.java yudao-module-wms/src/test/java/cn/iocoder/yudao/module/wms/service/inventory/WmsInventoryServiceImplTest.java yudao-server/pom.xml yudao-server/src/main/resources/application-dev.yaml yudao-server/src/main/resources/application-local.yaml yudao-server/src/main/resources/application-test.yaml (已删除) yudao-server/src/main/resources/application.yaml